diff --git a/404.html b/404.html new file mode 100644 index 0000000..b4fd0c6 --- /dev/null +++ b/404.html @@ -0,0 +1,339 @@ + + + + +404 Page not found + + + + + + + + + + + + + + + +
+ +
+
+

Not Found

+

This page does not exist

+
+ + + +
+
+ + + + + diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..764fc55 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +blog.hunterji.com diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..6c080e3 --- /dev/null +++ b/about/index.html @@ -0,0 +1,511 @@ + + + + +About + + + + + + + + + + + + + + + +
+ + + +
+
+
+ +
+ + +
+

+ About +

+ + +
+ + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

Hello, 我是开发者小橙。

+

公众号 - 开发者小橙

+ +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/about/weixin_qr.jpg b/about/weixin_qr.jpg new file mode 100644 index 0000000..cc2795b Binary files /dev/null and b/about/weixin_qr.jpg differ diff --git a/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_1024x0_resize_q75_box.jpg b/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_1024x0_resize_q75_box.jpg new file mode 100644 index 0000000..41deb22 Binary files /dev/null and b/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_1024x0_resize_q75_box.jpg differ diff --git a/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_480x0_resize_q75_box.jpg b/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_480x0_resize_q75_box.jpg new file mode 100644 index 0000000..bcd52f9 Binary files /dev/null and b/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_480x0_resize_q75_box.jpg differ diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..d1a0c5d --- /dev/null +++ b/archives/index.html @@ -0,0 +1,1281 @@ + + + + +Archives + + + + + + + + + + + + + + + +
+ +
+
+
+ + + + + + + + + + + + + + + + +
+

2021

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+

2020

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + +
+
+ + + + + diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..7800a85 --- /dev/null +++ b/categories/index.html @@ -0,0 +1,527 @@ + + + + +Categories + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

0 pages

+

Categories

+ +
+
+
+ + +
+ +
+ + + +
+
+ + + + + diff --git a/categories/index.xml b/categories/index.xml new file mode 100644 index 0000000..0c77627 --- /dev/null +++ b/categories/index.xml @@ -0,0 +1,10 @@ + + + + Categories on 开发者小橙 + https://blog.hunterji.com/categories/ + Recent content in Categories on 开发者小橙 + Hugo -- gohugo.io + en-us + + diff --git a/categories/page/1/index.html b/categories/page/1/index.html new file mode 100644 index 0000000..bf0e692 --- /dev/null +++ b/categories/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/categories/ + + + + + + diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000..4f43193 Binary files /dev/null and b/favicon.png differ diff --git a/img/avatar_hudb01ab6d48dedfdab966f93936237cd2_18789_300x0_resize_box_3.png b/img/avatar_hudb01ab6d48dedfdab966f93936237cd2_18789_300x0_resize_box_3.png new file mode 100644 index 0000000..19bf0eb Binary files /dev/null and b/img/avatar_hudb01ab6d48dedfdab966f93936237cd2_18789_300x0_resize_box_3.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..7672562 --- /dev/null +++ b/index.html @@ -0,0 +1,1389 @@ + + + + + +开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+
+
+ + Featured image of post 程序员如何快速验证业务需求? + + +
+ + +
+ + +
+

+ 程序员如何快速验证业务需求? +

+ + +

+ 作为一个程序员,听着攒劲的小曲,写着优雅的代码,加班加点终于写完了迭代中分配的模块需求。然后你跟别的同事一联调,却发现各种问题... +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post 如何快速入门新的编程语言和框架? + + +
+ + +
+ + +
+

+ 如何快速入门新的编程语言和框架? +

+ + +

+ 最近很多小伙伴儿通过各种途径咨询我关于学习技术方面的问题,我都一一回复了。这里也专门出一个总结,关于我个人这些年入门新的编程语言和框架的经验和技巧... +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+
+
+ + Featured image of post 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + + +
+ + +
+ + +
+

+ 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 +

+ + +

+ WebAssembly可以使用多种语言开发, 如果你更注重性能和安全性, 那么使用Rust整花活儿是个更好的选择~ +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+
+
+ + Featured image of post 使用Rust和WebAssembly整花活儿(一)——快速开始 + + +
+ + +
+ + +
+

+ 使用Rust和WebAssembly整花活儿(一)——快速开始 +

+ + +

+ WebAssembly可以使用多种语言开发, 如果你更注重性能和安全性, 那么使用Rust整花活儿是个更好的选择~ +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + +
+
+
+ + Featured image of post WebAssembly -- 未来前端开发的必备技能 + + +
+ + +
+ + +
+

+ WebAssembly -- 未来前端开发的必备技能 +

+ + +

+ WebAssembly是一种新的编码方式,可以在现代的网络浏览器中运行,它也被设计为可以与 JavaScript 共存,允许两者一起工作。 +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+
+ +
+ + +
+

+ python3 socket tcp example +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+ + + + +
+
+ + + + + diff --git a/index.xml b/index.xml new file mode 100644 index 0000000..4c14410 --- /dev/null +++ b/index.xml @@ -0,0 +1,15189 @@ + + + + 开发者小橙 + https://blog.hunterji.com/ + Recent content on 开发者小橙 + Hugo -- gohugo.io + en-us + Wed, 24 Jan 2024 23:53:17 +0000 + 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上) + https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/ + Wed, 24 Jan 2024 23:53:17 +0000 + + https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/ + <img src="https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/cover.png" alt="Featured image of post 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上)" /><h2 id="前言">前言</h2> +<p>裸辞后,我花了一个多月开发我的第一个独立开发产品<a class="link" href="https://redismate.hunterji.com/" target="_blank" rel="noopener" + >RedisMate</a>。其MVP版本上线五天,产品从没有排名,到冲到了国区mac app store开发工具类排名榜的前十,并持续有了几笔收入。</p> +<p>那么,我是怎么做的呢?</p> +<p>我是一个正在尝试独立开发的普通程序员,在此跟各位分享我的过程,不仅是我对自己的复盘,还希望能够给一些正在或准备尝试独立开发的兄弟一点参考。所有的过程都是按照我主观的想法去推进执行的,如果有更好的建议,欢迎交流!</p> +<p>本次分享分为上下两篇文章,分别为产品设计和产品推广两个主题。</p> +<h2 id="起源">起源</h2> +<p>首先,我并不是因为我有一个很棒的点子或产品,而选择去裸辞,去独立开发。</p> +<p>而是我因为别的原因选择了裸辞,然后开始把独立开发作为未来的一条路,并尝试去走。</p> +<p>所以,也可以说,一开始我是为了独立开发而去独立开发。</p> +<h2 id="产品选择">产品选择</h2> +<p>既然我没有一个很棒的点子去完成一个产品,那么我就开始去找。</p> +<h3 id="自身条件">自身条件</h3> +<p>首先,为了能够独立开发,我要审视自身,我会什么?</p> +<p>罗列下我的开发技能:</p> +<ul> +<li>前端:Vue、TS、&hellip;</li> +<li>后端:Go、Python、Rust</li> +<li>数据库:MySQL、Redis、PostgreSQL、ClickHouse、&hellip;</li> +<li>运维:Docker、MQ、Nginx、&hellip;</li> +<li>客户端/原生:Electron、Flutter、SwiftUI</li> +<li>硬件:esp32</li> +</ul> +<p>检查下其它独立开发必需技能:</p> +<ul> +<li>产品:自嗨水平</li> +<li>设计:自嗨水平</li> +<li>运营:几乎没有</li> +</ul> +<p>罗列下我熟悉的可以寻找需求的领域:</p> +<ul> +<li>开发</li> +<li>玩游戏</li> +<li>Linux日常办公</li> +<li>Mac日常办公</li> +</ul> +<p>我能在独立开发过程中投入多少资源:</p> +<ul> +<li>时间:全职</li> +<li>资金:起初的时候几乎完全不想投入一块钱,但起码要支付苹果个人开发者年费+服务器年费+域名年费</li> +</ul> +<h3 id="产品形态">产品形态</h3> +<p>那么,基于开发技能,得出我可以做的产品的形态:</p> +<ul> +<li>安卓app</li> +<li>ios app</li> +<li>Windows/Linux/macOS应用程序</li> +<li>SaaS</li> +<li>硬件</li> +<li>浏览器插件</li> +<li>编辑器插件</li> +<li>微信小程序</li> +</ul> +<p>但是考虑到希望以较小的成本来尝试独立开发,和希望能够短期得到收入来验证产品,那么就剩下了</p> +<ul> +<li>安卓app</li> +<li>ios app</li> +<li>Windows/Linux/macOS应用程序</li> +</ul> +<p>并且还都是离线应用,不需要服务器的支撑,没有太多服务器的额外成本,服务器仅仅需要最小配置用来备案即可。</p> +<p>考虑到付费方式的成本、上架的成本,再排除掉安卓app、Windows/Linux应用程序。</p> +<p>那么就只剩下</p> +<ul> +<li>ios app</li> +<li>macOS应用程序</li> +</ul> +<h3 id="产品需求">产品需求</h3> +<p>确定了产品形态,我就要考虑,到底要做什么产品呢?产品需求是什么?</p> +<p>如果我从当下很火但我完全不熟悉的领域去找需求,那我肯定没法挖掘真正的需求。</p> +<p>如果我找一个熟悉某个领域的人咨询,他并没有挖掘需求的能力,无法识别出真正的需求,这一点在我多年的工作中已经见识过太多太多次了,最后完完全全就是一个定制化项目,在市场上根本没有竞争力。</p> +<p>这就要从上述罗列的,我熟悉的领域去找。</p> +<p>所以我把目标放在了“开发”上。“开发”是我最熟悉的领域,在其中这么多年了,需求来源于我自己,我就是用户,我就可以直接验证这个需求是不是伪需求,这个功能是不是好功能。</p> +<p>并且,鉴于我三年前开发过一款开源的Redis GUI Client——<a class="link" href="https://github.com/hunter-ji/RedisFish" target="_blank" rel="noopener" + >RedisFish</a>,积累了一些经验,并且后来因为工作搁置了,导致一直没有完成也心有遗憾。</p> +<p>所以最终决定开发一款Redis GUI for Mac。</p> +<h2 id="市场调研">市场调研</h2> +<p>在决定产品之后,我先去问了一圈我的朋友和前同事,他们正在用的Redis GUI是什么。</p> +<p>得到的回复是如下产品:</p> +<ul> +<li>RedisDesktopManager</li> +<li>AnotherRedisDesktopManager</li> +<li>Medis2</li> +<li>RedisFish(被我安利用我自己的开源产品,哈哈&hellip;)</li> +</ul> +<p>其中RedisDesktopManager和AnotherRedisDesktopManager是最多的,毕竟是老牌的强劲产品!</p> +<p>然后,我又去翻了些论坛和帖子,发现有些人在发帖问mac上有哪些推荐的Redis GUI,但是哪怕是老外,也是如下选项居多:</p> +<ul> +<li>RedisDesktopManager</li> +<li>AnotherRedisDesktopManager</li> +<li>Medis2</li> +<li>RedisInsight</li> +</ul> +<p>接着,我也去了解了下近期新出的,比如Tiny RDM,听劝作品,真的很棒的,哈哈。</p> +<p>然后还有几个出现在比如gitee等平台的。</p> +<p>所以目前mac原生的也只有Medis2。</p> +<p>最后,我就要说那个经典台词了:没有能够满足我需求的!(不是&hellip;</p> +<h2 id="重新构思mvp版本">重新构思MVP版本</h2> +<p>到这个事件前,我已经开发完了既定的MVP版本,为v 1.1,此时的RedisMate是一个完成了基础Redis功能的App。</p> +<p>而我也已经怀着激动而又紧张的心情,在想如何推广MVP版本了。</p> +<p>但是,在我准备尝试推广的那个早上,在上厕所的时候,当时正在看《人性的弱点》。刚好看到书上讲了一个推销员的案例,推销员疯狂地讲述自己的产品怎么怎么好,让作者要赶紧买。</p> +<p>因此作者就在书上写道:</p> +<blockquote> +<p>人们其实什么东西都不需要。如果我们想买点什么,早就出门去买回来了。人们真正需要的,是解决问题的方式。人类永远都面临着种种问题,永远都需要这些问题的解决方案——如果销售人员能够证明其服务或产品可以帮助人们解决问题,不用推销,我们就会主动掏钱。对消费者而言,“主动买”比“被推销”的感觉好得多。</p> +</blockquote> +<p>所以,我就在想,我现在做的这是什么啊?!我能给其他人解决什么问题呢?凭什么要选择我的,而不是别的成熟产品?</p> +<p>我就开始重新构思MVP版本,希望能够加上独特的、创新的功能,能够真实解决用户问题的功能。</p> +<h2 id="创新">创新</h2> +<h3 id="寻找创新">寻找创新</h3> +<p>如何发现问题并解决问题?可以从经济学的四种不同类型的效用来入手:</p> +<ul> +<li>场所效益:使得原本无法接触的事物变得可接触</li> +<li>形式效用:通过重新排列现有部件使某物更有价值</li> +<li>时间工具:让事情变快的工具</li> +<li>拥有权利:去除中间人</li> +</ul> +<p>在此处,我的产品RedisMate上,最简单也是最快的的创新方式就是“让事情变得更快”。</p> +<p>我先明确Redis GUI使用频率最高的功能,然后从那个功能入手,让用户能够立竿见影感受到RedisMate的便捷。</p> +<p>那么,经过自己的总结,和跟朋友的沟通,我总结出,Redis GUI工具,使用频率从高到低的功能分别是:查(甚至是搜索)、删、改、增。</p> +<p>因此,我就从搜索功能入手。</p> +<p>我把RedisMate搜索功能的所有步骤罗列出来,然后思考,有哪些步骤可以减去?如何减少步骤?</p> +<p>最终就得到了现在“快速搜索”功能!</p> +<p>当Redis GUI窗口常驻时,一般最少需要约7步操作,那么在RedisMate上最少只要3步。</p> +<p>当Redis GUI仅打开没有连接任何服务器时,一般最少需要约9步操作,但是在RedisMate上最少只要3步。</p> +<p>当Redis GUI反复搜索固定key时,一般最少需要约4步,但是在RedisMate最少仅仅需要1步。</p> +<p>所以,我在RedisMate上针对搜索场景,进行了优化,并确实在推广时,得到了不少积极反馈。</p> +<p>并且,基于上述过程,优化了RedisMate的布局和一些细微的功能,让用户能够更直观更快去操作。</p> +<h3 id="挖掘需求">挖掘需求</h3> +<p>等我后面回过神的时候,才发现我一直有这个需求。</p> +<p>经常性,我要前后端一起开发,由于是微服务,多个节点并发开发和调试,每次都要开超多窗口。</p> +<p>我经常要切换窗口时,要愣一会儿思考一下,我要切什么窗口来着&hellip;</p> +<p>所以我就一直在想,能不能省略掉这些窗口,窗口自己呼出然后切回去!</p> +<p>现在RedisMate就可以了,这就解决了我曾经的问题。</p> +<p>然后,我就又想到曾经的一个抱怨,有时候在调试时,代码有点问题,或者业务链路较长,需要联调时间有点长,或者MQ驱动的业务节点产生key有点慢等等,key在我查的时候没有出现。我就要反复调出Redis GUI然后点点点后搜索再点点点,发现不对!调一下代码,再来一遍!</p> +<p>我当时就在想,要是GUI能帮我盯着就好了。</p> +<p>所以,我设计了&quot;Search Until Found&quot;功能,在得到朋友的肯定后,将其加入到了MVP版本中 。</p> +<h2 id="总结">总结</h2> +<p>首先,我很庆幸自己这些年学习和玩了不少技术,让我能够在想去独立开发的时候,不仅能够独立完成开发,还能有多种产品形态可以选择。并且我也有意识地去读技术之外的书,让我在其它领域有那么点点基础,只可惜没有学得更多。</p> +<p>其次,我觉得对我而言,苹果生态开发是当前成本最小的方式。</p> +<p>然后,我从我熟悉的领域入手,寻找自己的痛点,验证自己的需求,得到解决方案后,向身边的目标用户寻求验证。</p> +<p>最后,花一个多月开发完MVP版本,推出来验证自己的产品。</p> +<h2 id="最后">最后</h2> +<p>感谢你看完这篇文章,希望我的经过能够给你一些参考,帮助到你。</p> +<p>本次分享的下一篇文章——产品推广方面,我正在努力写,敬请期待并关注我以获取更新。</p> +<p>如果你对我的产品RedisMate感兴趣,非常欢迎<a class="link" href="https://redismate.hunterji.com/" target="_blank" rel="noopener" + >点击此处</a>了解和下载,或者在mac app store中搜索“RedisMate“。</p> +<p>最后,要感谢各位道上兄弟的关照,给了很多支持和反馈。</p> +<p>还要特别感谢那些购买高级版的兄弟,非常感谢你们的支持!</p> + + + + 程序员如何快速验证业务需求? + https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/ + Tue, 05 Dec 2023 15:56:29 +0000 + + https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/ + <img src="https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/cover.png" alt="Featured image of post 程序员如何快速验证业务需求?" /><h2 id="前言">前言</h2> +<p>设想一个场景:</p> +<p>你作为一个程序员,听着攒劲的小曲,写着优雅的代码,加班加点终于写完了迭代中分配的模块需求。然后你跟别的同事一联调,却发现各种问题。</p> +<p>你跟同事A说:“你得在这种情况下给我数据A。“</p> +<p>同事A一脸懵逼:“这种情况需求也没写啊,在这种情况下我也没有数据A啊!“</p> +<p>然后你和同事A一起顺着业务流程,找到了负责上层模块的同事B。</p> +<p>同事B一脸懵逼:“这个迭代中我没有这个模块的开发需求啊,这种情况下我也没有数据A啊!”</p> +<p>然后你和同事A、同事B一起顺着业务流程,找到了负责上层模块的&hellip;&hellip;</p> +<p>最后,你和同事ABC&hellip;一起找到了产品,产品一脸懵逼:“那没办法,就改需求吧&hellip;&hellip;”</p> +<p>你眼看着所剩无几的迭代时间,生无可恋地掏出手机,发个微信取消了这周末的相亲&hellip;</p> +<p>不知道程序员xdm有没有经历过这样的场景,如果各位程序员xd没有经历过这样的场景,那真的是恭喜你了,且行且珍惜!从毕业后参加工作开始,我是真的经历太多了,太痛了!</p> +<p>我之后不断学习各种方法,尝试去破解这样的困局。虽然说,工作中的问题很多,但是能解决一个是一个,后面再慢慢跟各位分享其他问题中我的解决方案。</p> +<p>所以,本篇文章将分享一波关于这类问题我的解决方案。谨代表个人主观观点。</p> +<h2 id="痛点">痛点</h2> +<p>需求是用户的痛点,但是在这个场景下,是程序员的痛点&hellip;</p> +<p>那么,仔细分析一下,这个场景下的痛点,到底是什么呢?我认为是如下两点:</p> +<ul> +<li>产品给到的需求无法自洽,且场景覆盖不够</li> +<li>开发团队的技术评审和技术文档不到位</li> +<li>开发各自按照模块开发,忽略外部环境,仅仅当各自开发完成后联调时候才会组装应用,发现问题</li> +</ul> +<p>当然,可能有程序员xd会疑问:这种问题不是靠严谨的开发流程就可以避免吗?</p> +<p>理论上来说是这样的,但是以我个人经历而言,不管是大团队还是小团队,总有很多不稳定的因素,比如说迭代周期、协作流程的规范性等等。</p> +<p>特别是在创业团队,或者有些项目特别着急的时候,经常是没有足够的时间给到团队的各个角色去充分准备的,甚至为了赶时间,会精简一些环节甚至直接去掉。这种时候出现这种问题导致改需求,往往是非常可怕的,后果也往往是开发团队加班。</p> +<h2 id="解决方案">解决方案</h2> +<p>我目前发现且实践后效果较好的方案,就是“曳光弹式开发”。</p> +<h3 id="什么是曳光弹">什么是曳光弹</h3> +<p>经常关注军事的小伙伴儿,应该了解,曳光弹是一种运用于军事场景的特殊子弹。</p> +<p>我们都知道武器发射时需要瞄准,比如枪就通过照门和准星来瞄准,而坦克和装甲车以及飞机都有专用的瞄准具,非常复杂。对于轻武器来说,一般攻击距离都比较近,使用自带的机械瞄准具或者外加的光学瞄准镜都足够,但是在距离稍远的时候瞄准镜显然不够。而对于飞机和战斗机来讲,在空中飞行时飞行姿态变化多样,<strong>有时候进攻的时间很短,需要快速射击并且调整弹道,这时候就需要可以发光的曳光弹。</strong></p> +<p>曳光弹正如其名,可以发光而且指示弹道。曳光弹的结构比一般子弹更加复杂。子弹的弹壳部分和一般子弹一样,前半部分是钢心或者铅心的弹头,但是在后部有一个空腔,一般称作曳光管,里面填充着曳光剂。曳光剂的成分还比较复杂,一般来说主要成分是镁粉和铝镁合金粉,用来燃烧,除此之外还有硝酸锶。这样一来,燃烧的时候硝酸锶就会发出红光。大家平时看到的很多曳光弹还有黄光和绿光,加入钠盐就会发出黄光,而加入铜盐就会发出绿光。除此之外还在表面加入一层过氧化钡,以保证曳光剂被点燃。</p> +<p>对于机枪手来说,如果射击距离较远,自然不能选择过于精确的设计方式,也就是说不能靠瞄准具来射击。而一般的机枪主要起压制和面杀伤作用,加入曳光弹就是机枪手更加快速方便的控制弹道,随时变化攻击的方向。如果没有曳光弹的话,射手根本无法发现自己的子弹弹道,也就很难去调整弹道。毕竟枪械射击温度升高之后,弹道会有所变化,不同的弹药不同的枪管也都会改变子弹的弹道,如果仅仅依靠枪械自身的瞄准具去调整反而会适得其反,而曳光弹就很好的解决了这个问题。</p> +<h3 id="在开发中的作用">在开发中的作用</h3> +<p>理解曳光弹本身在军事中的作用后,我们再来看曳光弹式开发如何在开发中起作用。</p> +<p>开发团队在接手到产品给到的业务需求后,特别是构建一些以前从未做过的东西时,对这个产品/功能的最终成效是模糊的。</p> +<p>程序员就像坦克上的机枪手一样,在尝试在黑暗中击中目标。但是如果像文章一开始的时候,大家先埋头写自己的模块,然后再联调,最终发现问题,感觉就像一上战场,大家都朝着各自理解的方向拼命清空弹夹,击中目标的寥寥无几,最后受伤的还是自己。</p> +<p>所以,此时,就需要先使用几颗曳光弹,去击中目标,在黑暗中划出轨迹,开发团队再调整方向,对着目标集中火力。</p> +<h3 id="如何应用">如何应用</h3> +<p>非常简单!步骤如下:</p> +<ol> +<li>找出级别最高的需求</li> +<li>以完成最基础的完整功能为目标,从前端到部署,开发一个可以运行的骨架</li> +<li>最后上相关需求的测试案例</li> +</ol> +<p>这样,射出一颗曳光弹,穿透客户端、后端、数据库、运维、测试等不同层面。一旦击中目标——即符合用户需求,后续的任务便大都是搬砖的活儿,去丰富这个骨架。</p> +<p>曳光弹并不是总能击中目标的,中途若是发现未能击中目标,便可在前期以极小成本去调整曳光弹的方向,继续发射曳光弹。</p> +<p>如果你要问,什么是“最基础的完整功能”,那么可以这么说,那么举个例子:前端不要任何样式,直接能够满足比如表单功能即可,后端不用任何校验,能够处理和传递数据即可。</p> +<h2 id="总结">总结</h2> +<p>为了解决文章开头的问题,需要使用曳光弹式开发,先开发一个骨架,确认满足需求后,即可继续丰富骨架,完成产品。</p> +<p>作为团队的一员,团队的每一个角色都很重要,程序员跟产品也是需要紧密协作,互帮互助,才能使得产品更好,也使得业绩更好。</p> + + + + 如何快速入门新的编程语言和框架? + https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/ + Wed, 29 Nov 2023 21:10:40 +0000 + + https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/ + <img src="https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/cover.jpg" alt="Featured image of post 如何快速入门新的编程语言和框架?" /><h2 id="一-前言">一. 前言</h2> +<p>最近很多小伙伴儿咨询我关于学习技术方面的问题,我都一一回复了。这里也专门出一个总结,关于我个人这些年入门新的编程语言和框架的经验和技巧,来跟大家分享一下,希望能够所有帮助。</p> +<p>本次分享分为两个时期:</p> +<ul> +<li>有编程经验时期</li> +<li>新手时期</li> +</ul> +<h2 id="二-有编程经验时期">二. 有编程经验时期</h2> +<p>当我有了一定开发经验后,去学习一个新的编程语言或者框架,我会注意如下两点:</p> +<h3 id="1-寻找共性">1. 寻找共性</h3> +<h4 id="编程语言">编程语言</h4> +<p>所有的编程语言或者框架,都不是凭空产生的。当前比较流行的语言,大都是类C语言,比如我们常见的Java、Python、JavaScript、Golang等等。</p> +<p>所以,我们学习的编程语言,除了特有的特性,大都是相似的,也就是它们的共性。我们只要理解和掌握这些类C语言的共性,就可以很快地掌握一门新的编程语言。</p> +<p>那么,哪些是共性呢?比如说变量、常量、运算符、判断、循环、函数、面向对象等等。</p> +<p>我们在学习一门新的编程语言的时候,是不是必然要学习如上这些共性的知识点?</p> +<p>那么,当你已经有一个编程语言的基础后,当你在学习和写代码的时候,这些共性可以直接使用你已学会的编程语言的知识去代入。</p> +<p>举个例子,比如我已经会Go了(不过,当你先学Go的话,可能不太知道面向对象是什么/狗头),要去学一下Python。</p> +<p>我需要做的就是代入:</p> +<ul> +<li> +<p>Go语言声明变量是<code>a := 1</code>,考虑到Python没有强类型,那么就直接<code>a = 1</code>就完事了。</p> +</li> +<li> +<p>Go语言声明函数是<code>func aFunction(username String) {}</code>,那么Python不过是换了个关键词,去掉了强类型,变成<code>def aFunctino(username):</code>,当然Python如果要申明类型也是可以的,写成<code>def login(username: str):</code>。</p> +</li> +<li> +<p>其中,比如运算符大都是一样的,判断也是类似。</p> +</li> +<li> +<p>但是突然你发现,怎么Python的循环不一样,然后发现Python的循环好方便,但是其实原理还是和Go一样的。甚至,当你遇到不知道怎么处理的时候,直接来一波<code>for (i := 0; i &lt; count; i++)</code>。当然,Python没法这么写。</p> +</li> +</ul> +<p>除了我举的这些例子,还有比如正则、数据类型、数据结构等等,都可以作为共性来代入。</p> +<h4 id="框架">框架</h4> +<p>其实,框架也是如此。此处以web框架为例,比如Python的Flask、Django、FastApi,Go的Gin,Node的express,Java的SpringBoot等,都是比较熟知的框架。</p> +<p>这些Web框架,都是依托于网络的,大都是http(s)请求,甚至其他TCP请求。我们可以只在网络层面来看,它们共性就是request和response。所以在网络层面就可以理解为,后端web框架就是一个接收request和response的东西。</p> +<p>那么,看具体框架的功能上,可以抽象出路由、中间件。</p> +<p>所以,对于一个web框架,其共性为request、response、路由、中间件。</p> +<p>因此,我们不管是使用Flask、Gin还是SpringBootd等,可以使用我们已有的框架知识去代入:</p> +<ul> +<li>如何接收request,比如获取request body、headers等</li> +<li>如何使用中间件处理,比如使用中间件鉴权、传递上下文等</li> +<li>如何配置路由,让接口对外暴露</li> +<li>如何返回response,比如返回http status code、json等</li> +</ul> +<p>那么,就可以完成一个新的web框架的入门了。</p> +<h4 id="其它">其它</h4> +<p>除了编程语言和框架,其实还有很多别的都是拥有共性,可以快速入门的,比如Nginx和Apache,比如Linux发行版本,比如Kong和ApiSix,比如不同的数据库等等。甚至最近我发现,wasm并不是前端特有的,EBPF也在用wasm。</p> +<h3 id="2-看官方文档">2. 看官方文档</h3> +<p>编程语言的情况还比较少,更多是学习框架时,一定要看官方文档。</p> +<p>网上虽然有很多教程,但是毕竟是二次加工的,可能存在信息遗漏、版本更新等等问题。</p> +<p>官方文档可能比较生涩,但是它是最全最新的一手文档,会非常有助于少走弯路。</p> +<p>再次强调,一定要看官方文档!</p> +<h2 id="二-新手时期">二. 新手时期</h2> +<p>当我还是新手的时候,我其实也常常苦恼于入门——教程看了好多遍,跟着教程敲了一遍又一遍,感觉还是只会基础,自己想写点什么却不会。</p> +<p>后来,当我入门一段时间后,我返回去想一想,才想明白一些技巧。</p> +<p>所以,以我的经历,总结如下步骤:</p> +<h3 id="1-仅学基础">1. 仅学基础</h3> +<p>一开始的时候,我也是不停地啃教程,不管是文档还是视频,但是发现我还是只会基础理论知识,那些高级特性案例都敲了,却并不明白什么意思。</p> +<p>后来,我就发现,对于我而言,并不需要学太多,只要学会基础即可,甚至基础也不用太深入。</p> +<h3 id="2-自己构思写一个项目">2. 自己构思写一个项目</h3> +<p>跟着教程敲代码,效果甚微。</p> +<p>要自己构思一个项目,比如写一个TODO,写一个命令行聊天工具,写一个博客等等简单的程序。最好是自己感兴趣的领域。</p> +<p>但是,需要注意的是,一定要自己去构思如何写:</p> +<ul> +<li>你准备写一个什么程序?要达到什么效果?</li> +<li>写这个程序有哪些功能?</li> +<li>这些功能都应该怎么实现?</li> +</ul> +<p>然后,直接开始写代码!哪怕只是创建了几个文件。</p> +<p>只有自己思考的,才能深刻理解和记忆。</p> +<h3 id="3-不会就去学不懂就复制过来用">3. 不会就去学,不懂就复制过来用</h3> +<p>当然,因为仅仅学了基础,你会发现很多功能并不会,甚至比如如何读取一个文件都不会。</p> +<p>如果这时候,遇到不会的,就当场去学,哪怕是高级特性,学到把不会的这个点能做出来为止。</p> +<p>如果一个功能不懂什么意思,搞不明白,那么直接复制过来,调试到能满足功能为止,然后将这段代码和这个场景记住。</p> +<h3 id="4-多写代码迈过门槛">4. 多写代码,迈过门槛</h3> +<p>我一直觉得写代码有一个门槛,在迈过门槛前,学习会非常困难,但是,一旦迈过这个门槛,你就会发现,学起来异常轻松,你所学的技术可以(相对)融会贯通。</p> +<p>那么迈过这个门槛的方法就是:重复2和3两个步骤,多写代码,多思考。</p> +<p>是的,不断重复,然后可能某一天,当你对你学的技术恍然大悟的时候,那么其实就是迈过了那个门槛。</p> +<p>然后,之后便是更深入地学习了。</p> +<h2 id="三-总结">三. 总结</h2> +<p>一路走来,我觉得最重要的是“多写代码,多思考”。不少代码写到最后甚至都是肌肉记忆了,但是代码还是会变的,自己思考的方式和过程,自己沉淀的知识才是最宝贵的,足以支撑我能够不断学习新的技术,适应新的模式。</p> + + + + 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + Thu, 26 Oct 2023 23:51:13 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(三)——Rust与JS交互》</a>中,讲述了Rust与JS的交互,包括Rust与JS的函数相互调用,比较炸裂的功能就是使用JS调用Rust的struct,JS本身连struct都没有,居然可以调用Rust的struct,这对于Rust开发者的开发体验而言,是真的很棒!</p> +<p>基于前面的系列文章,已经足以使用Rust开发一个完整的功能了。</p> +<p>但是,在前端引入wasm文件时,还是可能存在一些问题,比如wasm文件较大,导致网页访问时间较长,用户体验较差。本篇文章将会通过多种途径来减少Rust编译wasm文件的体积,以减少前端加载wasm文件的时间。</p> +<p>曾经一段时间,我一直用<a class="link" href="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/" target="_blank" rel="noopener" + >Go开发WebAssembly</a>,其编译后的wasm文件体积还是较大的,为了减少wasm文件的体积真是煞费苦心,1xMB大小的wasm文件真的是太痛了&hellip;&hellip;但是体积优化往往都会指向一条“充满魅惑的不归路”——换成Rust开发😆!如果你已经在用Rust开发WebAssembly了,那么恭喜你,对Go而言,在wasm体积上,你已经赢在起跑线上了。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="查看体积">查看体积</h2> +<p>在真正去减少体积前,我们需要来先看一下,当前情况下体积是多少,方便后续对比前后体积。</p> +<p>查看体积的方式有多种,这里推荐几个,(Linux和MacOS)使用其一即可。</p> +<h3 id="ls">ls</h3> +<p>可以使用<code>ls -l</code>或者<code>ll</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ll pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">-rw-r--r-- <span class="m">1</span> kuari staff 23K Jul <span class="m">20</span> 21:52 pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="stat">stat</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ stat pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"><span class="m">16777222</span> <span class="m">142141572</span> -rw-r--r-- <span class="m">1</span> kuari staff <span class="m">0</span> <span class="m">23347</span> <span class="s2">&#34;Jul 20 21:52:53 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="m">4096</span> <span class="m">48</span> <span class="m">0</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="wc">wc</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23347</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>以<code>wc</code>为例,当前该wasm文件体积为<strong>23347b</strong>。</p> +<h2 id="代码层面">代码层面</h2> +<blockquote> +<p>Link-Time Optimization (LTO) 是指在程序链接时进行的一种过程间优化(interprocedural optimization)。它允许编译器在链接阶段对多个编译单元进行优化,从而提高程序的性能、可靠性和安全性。</p> +</blockquote> +<p>从代码层面优化,主要是利用LTO(Link-Time Optimization)。</p> +<h3 id="代码内">代码内</h3> +<p>在<code>Cargo.toml</code>中开启LTO:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>开启LTO虽然能够减少编译后的体积,但是也会增加编译时间。</p> +<p>LTO开启后,默认是在减少一定程度的编译体积的情况下,要确保编译的时间。如果你的需求就是更小的体积,而不是较短的时间,那么,可以通过手动指定编译等级来让LTO作出改变。</p> +<p>在代码内可以使用如下等级:</p> +<ul> +<li><code>s</code>:默认的 LTO 等级。它会进行最基本的 LTO 优化,包括内联函数、函数重写、数据重排等</li> +<li><code>z</code>:最高级的 LTO 等级。它会进行更复杂的 LTO 优化,包括死代码消除、内存分配优化、安全性优化等</li> +</ul> +<p>那么可以在<code>Cargo.toml</code>中这么配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="nx">opt-level</span> <span class="p">=</span> <span class="s1">&#39;z&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>原始的文件体积是23347b,现在编译后看一下体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19879</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>很明显是减少体积!但是,使用<code>z</code>等级并不代表一定每次体积都会比<code>s</code>小的,有时候<code>s</code>也会比<code>z</code>小,这需要视代码情况而定。</p> +<h3 id="代码外">代码外</h3> +<p>在代码外,可以使用<a class="link" href="https://github.com/WebAssembly/binaryen" target="_blank" rel="noopener" + >wasm-opt</a>来进行优化,其可以对 WebAssembly 模块进行多方面的优化,当然本篇文章中重点在体积方面(挖个坑,后面再详聊/狗头)。并且wasm-opt可以对所有符合WebAssembly规范的wasm文件进行优化,所以,就算你不是Rust写的,那也可以用其进行优化。(想想我曾经Go写的wasm,也是有多一个法子可以优化一把了&hellip;&hellip;)</p> +<p>首先,来看一下wasm-opt的基本优化参数:</p> +<ul> +<li><strong>-o</strong>:指定优化后的模块输出文件</li> +<li><strong>-O</strong>:启用默认优化,等同于<code>-Os</code>参数</li> +<li><strong>-O0</strong>:不进行任何优化</li> +<li><strong>-O1</strong>:进行一些基本的优化,例如内联函数优化和死代码消除优化</li> +<li><strong>-O2</strong>:进行更为彻底的优化,例如函数重写、数据重排、内存分配优化等</li> +<li><strong>-O3</strong>:进行最为彻底的优化,包括一些可能影响程序功能的优化</li> +<li><strong>-O4</strong>:与 <code>-O3</code> 相同,但会启用更为激进的优化</li> +<li><strong>-Os</strong>:优化目标是减小代码大小,会进行一些可能影响性能的优化</li> +<li><strong>-Oz</strong>:与 <code>-Os</code> 相同,但会启用更为激进的优化</li> +</ul> +<p>基于本篇文章主题,此处将使用<code>-Os</code>和<code>-Oz</code>两种参数,其于上述&quot;代码内&quot;的等级是对应的。</p> +<p>此处以原始wasm文件,以<code>-Oz</code>参数来执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23194</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>再以上述开启“代码内“LTO编译后的wasm文件,以wasm-opt执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19871</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>总体而言,wasm文件的体积越来越小。只是当前我这里的案例,是沿用系列文章内容的代码,没有什么实际性复杂代码,再者本身体积已经很小了,所以不会特别有效果。</p> +<h2 id="网络层面">网络层面</h2> +<p>网络层面的话,就是众所周知的在网络传输时,客户端和服务端约定相同的压缩算法,然后服务端给出时进行压缩,客户端接收时进行解压。网络层面可以对传输报文进行压缩,但不丢失信息。</p> +<p>比如大家都很熟悉的gzip压缩算法,不过,压缩算法有好几种:</p> +<ul> +<li>gzip</li> +<li>compress</li> +<li>deflate</li> +<li>br</li> +</ul> +<p>其中gzip也是压缩率最高的了,此处就以gzip为例。</p> +<p>在网络层面,将wasm文件以gzip压缩,减少其在传输时的体积。虽然减少了传输时的体积,但是浏览器在拿到压缩后的数据,需要消耗一定性能来解密。</p> +<h3 id="开启gzip">开启GZIP</h3> +<p>开启GZIP其实简单,只要前后端约定好都用gzip就行了。</p> +<p>首先,前端请求wasm文件时,需要在request header中放入浏览器支持的压缩模式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Accept-Encoding: gzip, deflate +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,服务端收到这个请求后就可以给出服务端也支持的压缩模式,并告诉浏览器服务端将会用什么压缩模式。</p> +<p>跟浏览器通信的方式就是将信息塞到respone header里面:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Content-Encoding: gzip +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样就开启GZIP了。</p> +<p>然后,就是浏览器接收到response的body和header,知道后端使用gzip压缩的,那么浏览器就会自动用gzip来解压,拿到完整的数据了。</p> +<h3 id="服务端支持">服务端支持</h3> +<p>或许你会想问,浏览器能自动解密,那服务端怎么自动加密呢?要后端写代码让文件加密吗?</p> +<p>那当然不是了,直接让http server来完成这个操作。此处以耳熟能详的Nginx为例。</p> +<p>最简单的就是一行配置开启gzip了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span></code></pre></td></tr></table> +</div> +</div><p>也可以指定gzip的一些参数,比如可以加密的类型、最小加密长度等等:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span><span class="line"><span class="cl">gzip_types text/plain application/xml; +</span></span><span class="line"><span class="cl">gzip_proxied no-cache no-store private expired auth; +</span></span><span class="line"><span class="cl">gzip_min_length 1000; +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>更多的http server配置,可以去各自官方文档查阅。</p> +<h2 id="物理层面">物理层面</h2> +<p>你可能会惊奇,什么物理层面?!</p> +<p>没错,真就是物理层面——直接对wasm文件进行gzip物理压缩!哈哈,这个方法也真是绝了,我之前在Go开发wasm时,寻找减少体积的时候发现的,如果你的wasm已经优化得穷途末路了,不妨大胆试试这个方案。😆</p> +<p>还记得上面章节“网络层面“中,有个问题就是是否需要手动压缩,那么这里就是全程手动压缩和解压缩了,哈哈。</p> +<h3 id="物理压缩">物理压缩</h3> +<p>首先,是对wasm文件进行物理层面的gzip压缩,此处先使用原始的wasm(23347b):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gzip -c pkg/hello_wasm_bg.wasm &gt; pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,看一下其体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">10403</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果卓群,从23347b减少到了10403b!</p> +<p>然后来把上述“代码层面”的优化来一遍,看一下最后的体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">9237</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果更加卓群了,从19871b减少到了9237b了!</p> +<p>所以,此处就是对wasm文件进行物理压缩并存储,然后浏览器请求时,直接请求到<code>.gz</code>文件。</p> +<h3 id="物理解压">物理解压</h3> +<p>浏览器拿到<code>.gz</code>文件后,需要物理解压。</p> +<p>这里推荐使用<code>pako</code>这个前端库,对<code>.gz</code>文件进行解压:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">gunzipWasm</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;target.gz&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">arrayBuffer</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// A fetched response might be decompressed twice on Firefox. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// See https://bugzilla.mozilla.org/show_bug.cgi?id=610679 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">buffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x1F</span> <span class="o">&amp;&amp;</span> <span class="nx">buffer</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x8B</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">buffer</span> <span class="o">=</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">buffer</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>之后就可以直接使用了。</p> +<h2 id="buff叠加">BUFF叠加</h2> +<p>此处直接将上述所有方法都用起来,直接叠加buff,来看看当前(本系列文章积累的)这个案例能减少多少体积。在“物理层面”章节中,已经累加除了“网络层面”的buff了,所以可以直接使用其结果。而“网络层面”章节中,以gzip来压缩,将gzip的压缩率以40%来估算。</p> +<p>那么最终该案例的wasm体积将在5542b,压缩率大约在77%!</p> +<p>当然,还要算上一个初始的语言buff——Rust,使用Rust本身就已经导致wasm文件体积很小了。</p> +<h2 id="总结">总结</h2> +<p>本片文章中,从代码层面、网络层面、物理层面共三个层面介绍了对wasm文件的体积优化方案,其中共有四个方案。</p> +<p>最后,当前(本系列文章积累的)该案例叠加了所有buff之后,能够减少77%的体积,真的感觉挺棒的了,哈哈。</p> +<p>希望能够对各位有所帮助。</p> + + + + 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + Tue, 27 Jun 2023 00:11:12 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(二)——DOM和类型转换》</a>中,描述了使用Rust操作DOM,并实现Rust与JS类型转换的多种方法。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>基于上一篇文章中,Rust与JS的类型转换的多种方法,本篇文章继续深入Rust与JS的交互。</p> +<p>首先,Rust与JS的类型转换,可以实现变量的传递,那么变量是要用在哪里呢?那必然是函数了!</p> +<p>所以,本篇文章来讲述一下Rust与JS的函数相互调用,基于此,可以实现大量日常功能开发。</p> +<p>并且,还将会讲述一下,如何导出Rust的struct给JS调用。</p> +<p>是的,没错,在JS调用Rust的struct!一开始看到这个功能的时候,我的脑子是有点炸裂的&hellip;&hellip;😳</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="函数的相互调用">函数的相互调用</h2> +<h3 id="js调用rust函数">JS调用Rust函数</h3> +<p>其实,在本系列文章的第一篇中,就是使用的JS调用Rust函数作为案例来演示的,这里依然以此为例,主要讲一下要点。</p> +<p>首先,声明一个Rust函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处需要注意的要点如下:</p> +<ul> +<li>引入<code>wasm_bindgen</code></li> +<li>声明一个函数,使用<code>pub</code>声明</li> +<li>在函数上使用<code>#[wasm_bindgen]</code>宏来将Rust函数导出为WebAssembly模块的函数</li> +</ul> +<p>接着,编译成wasm文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,在JS中调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server,在浏览器的控制台中可以看到<code>the result from rust is: 3</code>,表明调用成功!</p> +<h3 id="rust调用js函数">Rust调用JS函数</h3> +<p>###1 指定JS对象</p> +<p>在Rust中调用JS函数,需要进行指定JS对象,也就是说,得明确告诉Rust,这个JS函数是从JS哪儿拿来的用的。</p> +<p>主要在于下面两个方式:</p> +<ul> +<li><em><strong>js_namespace</strong></em>: 是一个可选的属性,用于指定一个JavaScript命名空间,其中包含将要在wasm模块中导出的函数。如果没有指定<code>js_namespace</code>,则所有的导出函数将被放置在全局命名空间下。</li> +<li><em><strong>js_name</strong></em>: 是另一个可选属性,它用于指定JavaScript中的函数名称。如果没有指定<code>js_name</code>,则导出函数的名称将与Rust中的函数名称相同。</li> +</ul> +<p>###2 JS原生函数</p> +<p>对于一些JS原生函数,在Rust中,需要去寻找替代方案,比如我们上一篇文章中讲的<code>console.log()</code>函数,是不是觉得好麻烦啊!</p> +<p>那么,你想直接在Rust中调用JS原生函数吗?!</p> +<p>此处,就以<code>console.log()</code>函数为例,直接在Rust中引入并调用,免去替代方案的烦恼。</p> +<p>首先,给出Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如上代码中,<code>call_js_func</code>函数,顾名思义,此处是调用了js函数,并传入参数<code>hello, javascript!</code>。</p> +<p>那么,<code>call_js_func</code>函数上方的代码,我们来一步步解析一下:</p> +<ol> +<li>第一行代码<code>#[wasm_bindgen]</code>是Rust的属性,它告诉编译器将函数导出为WebAssembly模块</li> +<li><code>extern &quot;C&quot;</code>是C语言调用约定,它告诉Rust编译器将函数导出为C语言函数</li> +<li><code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的console对象</li> +<li><code>fn log(message: &amp;str)</code>是一个Rust函数,它接受一个字符串参数,并将其打印到JavaScript中的console对象中</li> +</ol> +<p>此处与JS交互的关键是<code>js_namespace</code>。在Rust中,<code>js_namespace</code>是用于指定JavaScript命名空间的属性。在WebAssembly中,我们可以通过它将函数绑定到JavaScript中的对象上。</p> +<p>在上述代码中,<code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的<code>console</code>对象。这意味着在JS中使用<code>console.log()</code>函数来调用Rust中的<code>log()</code>函数。</p> +<p>因此,类似的原生函数,都可以使用该方法来实现调用。</p> +<p>最后,我们在JS中调用下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">call_js_func</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">call_js_func</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以在浏览器的控制台中看到<code>hello, javascript!</code>。妙啊!</p> +<p>其实对于<code>console.log()</code>而言,还有另一种调用方式,那就是使用<code>js_namespace</code>和<code>js_name</code>同时指定。</p> +<p>或许,你会问,这有什么不同吗?是的,这有些不同。</p> +<p>不知道你是否发现,当前这个案例中,指定了<code>js_namespace</code>为<code>console</code>,但是真实执行的函数是<code>log()</code>,那么这个<code>log</code>函数的指定,其实是体现在Rust中同样的函数名<code>log</code>。也就是说,该案例的<code>log()</code>就是<code>console.log()</code>中的<code>log()</code>。</p> +<p>我们来换个名字看看,将原来的<code>log()</code>换成<code>log2()</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log2</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log2</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译后去控制台看看,就会看到报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">TypeError: console.log2 is not a function +</span></span></code></pre></td></tr></table> +</div> +</div><p>因此,当我们使用<code>js_namespace</code>和<code>js_name</code>结合的方式,在此处是可以进行自定义函数名的。</p> +<p>看一下Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console, js_name = log)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log_str</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log_str</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,重新定义了一个函数<code>log_str</code>,但是其指定了<code>js_namespace = console</code>和<code>js_name = log</code>,那么此处,就可以使用自定义的函数名。</p> +<p>直接编译后,在控制台看一下,可以直接看到正常输出:<code>hello, javascript!</code>。</p> +<p>总结一下,如果没有指定<code>js_name</code>,则 Rust 函数名称将用作 JS 函数名称。</p> +<p>###3 自定义JS函数</p> +<p>在一定场景下,需要使用Rust调用JS函数,比如对于一些对于JS而言更有优势的场景——用JS操作DOM,用Rust计算。</p> +<p>首先,创建一个文件<code>index.js</code>,写入一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">addIt</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当前的文件结构关系如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── Cargo.lock +</span></span><span class="line"><span class="cl">├── Cargo.toml +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── index.html +</span></span><span class="line"><span class="cl">├── index.js +</span></span><span class="line"><span class="cl">├── pkg +</span></span><span class="line"><span class="cl">│   ├── README.md +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.js +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">│   └── package.json +</span></span><span class="line"><span class="cl">├── src +</span></span><span class="line"><span class="cl">│   └── lib.rs +</span></span><span class="line"><span class="cl">└── target +</span></span><span class="line"><span class="cl"> ├── CACHEDIR.TAG +</span></span><span class="line"><span class="cl"> ├── debug +</span></span><span class="line"><span class="cl"> ├── release +</span></span><span class="line"><span class="cl"> └── wasm32-unknown-unknown +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>index.js</code>和<code>lib.rs</code>,以及<code>hello_wasm_bg.wasm</code>都是不在同一级别的,<code>index.js</code>都在其它两个文件的上一级。记住这个机构关系!</p> +<p>然后,在<code>lib.rs</code>中,指定函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl">##<span class="p">.</span><span class="o">/</span><span class="n">index</span><span class="p">.</span><span class="n">js</span><span class="s">&#34;)] +</span></span></span><span class="line"><span class="cl"><span class="s">extern &#34;</span><span class="n">C</span><span class="s">&#34; { +</span></span></span><span class="line"><span class="cl"><span class="s"> fn addIt(m: i32, n: i32) -&gt; i32; +</span></span></span><span class="line"><span class="cl"><span class="s">} +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>raw_module = &quot;../index.js&quot;</code>的意思是,指定对应的<code>index.js</code>文件,大家应该清楚,此处指定的是刚刚创建的<code>index.js</code>。<code>raw_module</code>的作用就是用来指定js文件的。</p> +<p>这段代码在前端,可以等同于:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addIt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;../index.js&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样在前端都不用引入了,直接在Rust中引入了,感觉还有点奇妙的。</p> +<p>接着,在Rust调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addIt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在前端调用,编译后,在浏览器的控制台中可以看到输出结果了!</p> +<p>总结一下,这里有几个注意点:</p> +<ol> +<li>JS的函数必须要export,否则将无法调用;</li> +<li><code>raw_module</code>只能用来指定相对路径,并且,大家可以在浏览器的控制台中注意到,此处的<code>../</code>的相对路径,其实是以wasm文件而言的相对路径,这里一定要注意呀!</li> +</ol> +<h2 id="js调用rust的struct">JS调用Rust的struct</h2> +<p>现在,来点炸裂的,JS调用Rust的struct?!</p> +<p>JS中连struct都没有,这玩意儿导出来会是什么样,得怎么在JS中调用呢?!</p> +<p>首先,定义一个struct,并且声明几个方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(constructor)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_user</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;name is : </span><span class="si">{}</span><span class="s">, age is : </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="p">).</span><span class="n">as_str</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">set_age</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">age</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,声明了一个struct名为<code>User</code>,包含<code>name</code>和<code>age</code>两个字段,并声明了<code>new</code>、<code>print_user</code>和<code>set_age</code>方法。</p> +<p>其中还有一个未见过的<code>#[wasm_bindgen(constructor)]</code>,<code>constructor</code>用于指示被绑定的函数实际上应该转换为调用 JavaScript 中的 new 运算符。或许你还不太清晰,继续看下去,你就会明白了。</p> +<p>接着,在JS中调用这个struct,和其方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">addIt2</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">User</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;kuari&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">set_age</span><span class="p">(</span><span class="mi">21</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">print_user</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到,这里的用法就很熟悉了!</p> +<p>大概想一下,在Rust中要如何调用?也就是直接new一个——<code>User::new('kuari', 20)</code>。</p> +<p>此处在JS中,也是如此,先new一个!</p> +<p>然后很自然地调用struct的方法。</p> +<p>编译后,打开浏览器,可以在控制台看到输出:<code>name is : kuari, age is : 21</code>。</p> +<p>其实,或许大家会很好奇,起码我是非常好奇的,Rust的struct在JS中到底是一个怎样的存在呢?</p> +<p>这里直接添加一个<code>console.log(user)</code>,就可以在输出看到。那么到底在JS中是一个怎样的存在呢?请各位动手打印一下看看吧!:P</p> +<h2 id="总结">总结</h2> +<p>本篇文章中,主要讲述了Rust与JS的交互,体现在Rust与JS的相互调用,这是建立在<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >上一篇文章</a>中类型转换的基础上的。</p> +<p>Rust与JS的函数相互调用的学习成本还是较大的,而且对比Go写wasm,Rust的颗粒度是非常细的,几乎可以说是随心所欲了。</p> +<p>比较炸裂的就是Rust的struct导出给JS用,这对于Rust与JS的交互而言,还是非常棒的体验。</p> + + + + 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + Sun, 18 Jun 2023 18:18:31 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/72" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(一)——快速开始》</a>中,描述了如何创建项目和快速生成wasm并在前端中使用,迈出了整花活儿的第一步。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +<li>web-sys 0.3.64</li> +</ul> +<h2 id="dom">DOM</h2> +<h3 id="配置依赖">配置依赖</h3> +<p>要操作DOM,需要引入新的依赖<code>web-sys</code>,因此,可以配置<code>Cargo.toml</code>中依赖如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>你或许会好奇,这个<code>features</code>是什么,讲真,我一开始很好奇,又没看到什么特别的说明,试错才发现,原来是要手动引入功能依赖&hellip;比如说,当你需要在Rust中使用JS的<code>console</code>,那么你需要在<code>features</code>中加入<code>console</code>。</p> +<h3 id="获取document">获取Document</h3> +<p>在Rust中使用<code>Document</code>,我们需要按照上一步的说明,添加<code>features</code>。那么这里有一个依赖关系,首先在Rust中获取<code>window</code>,然后再获取<code>document</code>。</p> +<p>因此,添加<code>features</code>后如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后在<code>lib.rs</code>中创建一个函数,用来调用<code>document</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>那么,现在就是在Rust中解锁了<code>document</code>,就可以在前端为所欲为了!</p> +<h3 id="操作element">操作Element</h3> +<p>那么开始操作一波,首先得获取到<code>Element</code>&hellip;&hellip;</p> +<p>是的,你没有想错,继续来添加<code>features</code>吧,此处要添加一个<code>Element</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>ok,那么继续。此处设定函数传入两个参数<code>selector</code>和<code>message</code>,然后通过<code>selector</code>获取element,更新值为<code>message</code>参数的值。完整函数如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">(</span><span class="n">selector</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">document</span><span class="p">.</span><span class="n">query_selector</span><span class="p">(</span><span class="n">selector</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load element&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">element</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">element</span><span class="p">.</span><span class="n">set_inner_html</span><span class="p">(</span><span class="n">message</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">panic!</span><span class="p">(</span><span class="s">&#34;Failed to set inner html&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编译">编译</h3> +<p>将写完的Rust项目编译成wasm:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在html中调用">在html中调用</h3> +<p>基于上一篇文章的项目中的html,此处添加一个<code>div</code>,id为<code>message</code>,添加调用wasm的<code>update_message</code>函数,代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;message&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">update_message</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> <span class="c1">// 引入update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">update_message</span><span class="p">(</span><span class="s1">&#39;#message&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;h1&gt;Hello, Rust!&lt;/h1&gt;&#39;</span><span class="p">);</span> <span class="c1">// 调用update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在浏览器验证">在浏览器验证</h3> +<p>启动一个http server,然后在浏览器查看,可以看到在页面上出现一个<code>h1</code>标签的<code>Hello, Rust!</code>。</p> +<h3 id="发现更多方法">发现更多方法</h3> +<p>按照文章来写的过程中,你应该会发现一个问题——怎么这些方法没有补全?!</p> +<p>是的,没错的,(至少我发现)当前<code>web-sys</code>并没有补全,所以只能结合开发者优秀的前端技能和丰富的<a class="link" href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/" target="_blank" rel="noopener" + >官方文档</a>来开发了。</p> +<h2 id="rust与js的类型相互转换">Rust与JS的类型相互转换</h2> +<p>对于wasm而言,性能固然是提升的,但是类型转换一直是个问题。当大量数据需要在wasm/js中进行类型转换时,这对性能来说,真的是个灾难。之前在使用go开发wasm时,就遇到过这样的问题,需要用官方的方法来进行手动类型转换,然而wasm处理的是一个很大的数据量&hellip;&hellip;</p> +<p>不过好在Rust的类型支持真的挺丰富的!</p> +<h3 id="基础类型">基础类型</h3> +<p>基础类型挺简单的,而且Rust的范性也很好地支持了很多类型。如下是基础类型映射表:</p> +<table> +<thead> +<tr> +<th>Rust类型</th> +<th>JavaScript类型</th> +</tr> +</thead> +<tbody> +<tr> +<td>i8</td> +<td>number</td> +</tr> +<tr> +<td>i16</td> +<td>number</td> +</tr> +<tr> +<td>i32</td> +<td>number</td> +</tr> +<tr> +<td>i64</td> +<td>BigInt</td> +</tr> +<tr> +<td>u8</td> +<td>number</td> +</tr> +<tr> +<td>u16</td> +<td>number</td> +</tr> +<tr> +<td>u32</td> +<td>number</td> +</tr> +<tr> +<td>u64</td> +<td>BigInt</td> +</tr> +<tr> +<td>f32</td> +<td>number</td> +</tr> +<tr> +<td>f64</td> +<td>number</td> +</tr> +<tr> +<td>bool</td> +<td>boolean</td> +</tr> +<tr> +<td>char</td> +<td>string</td> +</tr> +<tr> +<td>&amp;str</td> +<td>string</td> +</tr> +<tr> +<td>String</td> +<td>string</td> +</tr> +<tr> +<td>&amp;[T] 例如:&amp;[u8]</td> +<td>[T] 例如:Uint8Array</td> +</tr> +<tr> +<td>Vec<T></td> +<td>Array</td> +</tr> +</tbody> +</table> +<h3 id="基础类型转换示例">基础类型转换示例</h3> +<p>在<code>lib.rs</code>文件中,创建一个函数,挑选几个类型作为参数传入,然后将其读取并打印:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到该函数传入了JS的<code>number</code>、<code>boolean</code>、<code>Uint8Array</code>和<code>Array</code>四个类型的参数。</p> +<p>然后编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在前端中引入函数并调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_values</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_values</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">,</span> <span class="nx">jsBoolean</span><span class="p">,</span> <span class="nx">jsUint8Array</span><span class="p">,</span> <span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server并打开浏览器,在控制台可以看到&hellip;看不到?!</p> +<p>是的,没错,Rust的<code>println!</code>只会将打印的内容发送到Rust的标准输出流,而不是前端的控制台。如果想在控制台中打印,那么需要调用JS的<code>console</code>了。</p> +<p>使用新的功能,第一步就是添加<code>features</code>,<code>Cargo.toml</code>中添加<code>console</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在Rust中调用<code>console.log()</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="s">&#34;Hello, Rust!&#34;</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处将其封装成一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">console_log</span><span class="p">(</span><span class="n">message</span>: <span class="nb">String</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,将示例函数的<code>println</code>改成<code>console_log()</code>和<code>format!</code>,函数代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,编译之后,打开浏览器,就可以在控制台看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">js number: 10 +</span></span><span class="line"><span class="cl">js boolean: true +</span></span><span class="line"><span class="cl">js Uint8Array item: 1 +</span></span><span class="line"><span class="cl">js Uint8Array item: 2 +</span></span><span class="line"><span class="cl">js Uint8Array item: 3 +</span></span><span class="line"><span class="cl">js number array item: 30 +</span></span><span class="line"><span class="cl">js number array item: 40 +</span></span><span class="line"><span class="cl">js number array item: 50 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="通用类型">通用类型</h3> +<p>Rust中提供了一个通用的类型——JsValue,可以作为任何JS类型。</p> +<p>这里给一个简单的案例,设置一个函数,使用<code>JsValue</code>作为参数传入,并打印。</p> +<p>创建函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_js_value</span><span class="p">(</span><span class="n">val</span>: <span class="nc">JsValue</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">val</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译成wasm文件。</p> +<p>在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_js_value</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsBoolean</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsUint8Array</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在html中,传入了不同类型的参数,但是在浏览器的控制台中可以看到,将所有不同类型的参数都打印出来了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">JsValue(10) +</span></span><span class="line"><span class="cl">JsValue(true) +</span></span><span class="line"><span class="cl">JsValue(Uint8Array) +</span></span><span class="line"><span class="cl">JsValue([30, 40, 50]) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="result">Result</h3> +<p><code>Result</code>在Rust中是一个很重要的存在,经常写Rust的话,也不想在写WebAssembly时改变开发习惯。</p> +<p>其实对于JS而言,<code>Result</code>可以直接在<code>catch</code>中捕获到,只是说,这里我们需要定义好参数类型。</p> +<p>####1 使用Result返回报错</p> +<p>首先来一个只返回报错的场景:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">only_return_error_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>这里返回类型是<code>Result</code>,但是仅仅返回了一个错误。值得注意的是,这里的报错使用的类型是<code>JsError</code>,当然,这里也可以使用<code>JsValue</code>。</p> +<p>然后在html调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">only_return_error_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;1 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;100 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>那么,在浏览器的控制台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">100 is ok +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 使用Result返回正常值和错误</p> +<p>那么,如果想既返回正常值,也想返回错误呢?Rust返回一个<code>Result</code>是没有问题,那么JS怎么解析呢?</p> +<p>直接上Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_all_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>该函数,获取到参数后,如果满足条件,加10后返回,否则报错。</p> +<p>那么看看html中如何调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_all_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,正常获取就行了&hellip;&hellip;/捂脸哭</p> +<p>这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>最后,就是在浏览器的控制台中看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">get 110 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="直接引入js类型">直接引入JS类型</h3> +<p>如果你想更直接一点,那么可以直接引入JS类型!这里主要是利用<code>js-sys</code>这个依赖,可以在<a class="link" href="https://docs.rs/js-sys/latest/js_sys/" target="_blank" rel="noopener" + >官方文档</a>上看到很多JS的类型和函数,直接引入即可使用。当然,一定场景下,直接引入的类型,是需要手动转换类型的。</p> +<p>####1 配置依赖</p> +<p>在<code>Cargo.toml</code>中添加<code>js-sys</code>依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="nx">js-sys</span> <span class="p">=</span> <span class="s2">&#34;0.3.61&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 Uint8Array</p> +<p>首先以<code>Uint8Array</code>举例,在<code>lib.rs</code>头部引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Uint8Array</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后创建一个函数,参数和返回都是<code>Uint8Array</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_uint8_array</span><span class="p">(</span><span class="n">js_arr</span>: <span class="nc">Uint8Array</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Uint8Array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// new Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">new_with_length</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Uint8Array -&gt; vec +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">)</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">to_vec</span><span class="p">().</span><span class="n">iter</span><span class="p">().</span><span class="n">enumerate</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Avoid type conversion +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">js_arr</span><span class="p">.</span><span class="n">length</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">get_index</span><span class="p">(</span><span class="n">index</span><span class="p">)));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// vec -&gt; Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">arr2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">from</span><span class="p">(</span><span class="n">vec</span><span class="p">.</span><span class="n">as_slice</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">arr2</span><span class="p">.</span><span class="n">clone</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">arr</span><span class="p">.</span><span class="n">set_index</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>忽略该函数中无意义的逻辑和<code>arr</code>变量的警告,只是为了演示用法。</p> +</blockquote> +<p>可以在代码中看到,直接引入的<code>Uint8Array</code>有自己的方法,一定场景下,需要转换类型,但是最好避免进行类型转换,而直接使用其自带的方法。</p> +<p>这里可以简要总结下,就是最好一定场景内全部使用直接引入的JS类型,或者直接全部使用Rust类型来代替JS类型,两者都存在场景下,手动转换类型是件很糟糕的事。</p> +<p>####3 Date</p> +<p><code>Date</code>类型,在上面的篇章中都没有提及,这里可以直接引入JS的<code>Date</code>类型来使用。</p> +<p>首先是引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Date</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,创建一个函数,返回时间戳:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_time</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Date</span>::<span class="n">new_0</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">date</span><span class="p">.</span><span class="n">get_time</span><span class="p">()</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_time</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;current time: &#39;</span><span class="p">,</span> <span class="nx">return_time</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在浏览器的控制台中,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">current time: 1686979932833 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="总结">总结</h2> +<p>本文中,主要讲述了如何使用Rust来实现DOM操作,读者可以根据方法自己去找到合适的方法,来实现自己的场景。其次,还讲述了Rust与JS的类型转换,从基础的各自类型的映射,到Rust独有的<code>Result</code>,到直接引入JS类型。当然这里需要注意的是,直接引入JS类型和Rust的基础类型映射JS类型这两种方法尽量不要混用,混用会导致需要手动类型转换,造成性能损耗。</p> +<p>至此,又向Rust和WebAssembly整花活儿迈进了一步~😼</p> + + + + 使用Rust和WebAssembly整花活儿(一)——快速开始 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + Wed, 14 Jun 2023 17:32:28 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(一)——快速开始" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<p>之前写过一篇文章,是关于如何使用golang来开发WebAssembly的——<a class="link" href="https://blog.hunterji.com/p/webassembly--%e6%9c%aa%e6%9d%a5%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e7%9a%84%e5%bf%85%e5%a4%87%e6%8a%80%e8%83%bd" target="_blank" rel="noopener" + >WebAssembly:未来前端开发的必备技能</a>。</p> +<p>Rust和Go都可以用来开发WebAssembly,但它们有各自的优势和劣势。</p> +<p>Rust的优点:</p> +<ul> +<li>更快的性能和更小的二进制文件</li> +<li>更好的内存安全性</li> +</ul> +<p>Go的优点:</p> +<ul> +<li>更容易上手和学习</li> +<li>更好的生态系统和社区支持</li> +</ul> +<p>综合来说,如果你更注重性能和内存安全性,那么Rust可能是更好的选择。而如果你更注重开发效率和易用性,那么Go可能更适合你。当然,实际情况还需要根据具体的项目需求和团队情况来选择。</p> +<p>因为一些工作需求,最近整了些rust的花活儿,这里系统地记录一下。当你遇到更注重性能和内存安全性的场景,希望这能有帮助。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="创建项目并添加依赖">创建项目并添加依赖</h2> +<p>此处默认已经安装Rust,需要安装的小伙伴儿可以参考<a class="link" href="https://www.rust-lang.org/tools/install" target="_blank" rel="noopener" + >官网</a>。</p> +<p>使用Cargo创建一个名为<code>hello-wasm</code>的项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo new --lib hello-wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>进入项目,打开文件<code>Cargo.toml</code>,添加依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="更新librs">更新lib.rs</h2> +<p>默认创建项目中,存在一个名为<code>lib.rs</code>的文件,将内容全部替换成:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="编译">编译</h2> +<p>至此,我们创建了一个最简单的功能——一个返回两个整数相加结果的函数。</p> +<p>然后我们可以进行编译了,编译之前需要安装一个工具<code>wasm-pack</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo install wasm-pack +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>编译完成之后,将会多出来一个<code>pkg</code>文件夹,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pkg +</span></span><span class="line"><span class="cl">├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">├── hello_wasm.js +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">└── package.json +</span></span></code></pre></td></tr></table> +</div> +</div><p>虽然文件很多,但是首先我们看到了我们所需要的wasm文件,并且,根据go的wasm引入方式,这里我们或许会需要用到js文件。</p> +<h2 id="前端引入">前端引入</h2> +<p>为了方便最快校验,直接在<code>hello-wasm</code>项目中创建<code>index.html</code>文件,来进行前端引入。</p> +<h3 id="创建indexhtml">创建index.html</h3> +<p>那么,首先,创建<code>index.html</code>文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错!这是一场标准的开局!😼</p> +<h3 id="引入wasm">引入WASM</h3> +<p>其实不同于go语言的wasm引入方式,Rust更希望直接引入js文件,而不是让开发者手动引入wasm文件。</p> +<p>这里使用js引入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="完整代码">完整代码</h3> +<p>完整的html代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="验证">验证</h2> +<p>这里可以快速起一个http server,这里我选择使用<code>http-server</code>,也可以使用<code>python3 -m http.server</code>这样的方式,看怎么各自的使用习惯。</p> +<p>那么,启动http server:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">http-server +</span></span></code></pre></td></tr></table> +</div> +</div><p>打开浏览器,访问<code>http://localhost:8080</code>,打开调试器,即可看到输出<code>the result from rust is: 3</code>,这就意味着迈出了整花活儿的第一步!</p> +<p><img src="https://user-images.githubusercontent.com/25321169/245747861-4fb71bd2-9f90-41b3-ada8-6e1d930044d4.png" + + + + loading="lazy" + + alt="rust_wasm" + + +></p> +<h2 id="常见问题">常见问题</h2> +<h3 id="前端报响应类型错误">前端报响应类型错误</h3> +<p>详细报错如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Failed to load module script: The server responded with a non-JavaScript MIME type of &#34;application/wasm&#34;. Strict MIME type checking is enforced for module scripts per HTML spec. +</span></span></code></pre></td></tr></table> +</div> +</div><p>当引入WebAssembly生成的js文件时,可能会遇到这个报错。报错乍一看是http server的响应问题,或者搜索时候,也会有帖子说这是一个response问题。</p> +<p>实际上,当按照这个文章一步步操作时是不会有这个问题的,是因为本文的编译参数是直接解决了这个问题的。当我自己摸索的时候,解决这个问题真的是看到人都麻了……</p> +<p>关键在于编译命令的参数:<code>--target</code>。</p> +<p>当没有设置这个参数时,默认的参数其实是<code>--target bundler</code>,其是编译成给webpack之类的脚手架使用的。因此这里使用<code>—target web</code>,则是使其编译成可直接在web中使用。</p> +<p>相关参数如下:</p> +<ul> +<li><strong>bundler</strong>:编译成给webpack之类的脚手架使用</li> +<li><strong>web</strong>:编译成web可直接使用</li> +<li><strong>nodejs</strong>:编译成可通过require来加载的node模块</li> +<li><strong>deno</strong>:编译成可通过import加载的deno模块</li> +<li><strong>no-modules</strong>:跟web类似,但是更旧,且不能使用es模块</li> +</ul> +<h3 id="直接引入wasm文件">直接引入wasm文件</h3> +<p>若此时尝试直接引入wasm文件,而不是使用本文所述的方式,那么你会发现,也是可行的!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;./pkg/hello_wasm_bg.wasm&#34;</span><span class="p">),</span> <span class="p">{}).</span><span class="nx">then</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;the result from rust is: &#39;</span><span class="p">,</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,当前是可行的,但是当引入了一些别的比如dom之类的,就坏起来了……</p> +<h3 id="更新的wasm引入方式">更新的wasm引入方式</h3> +<p>上一问题中,且不说是否可以直接引入wasm文件,这里仅说一下,<code>instantiateStreaming</code>这个方法。这是一个更新的方法,无需转成<code>arrayBuffer</code>,这也是摸索Rust整活儿时候发现的。如果在别的语言引入wasm,请使用这个<a class="link" href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming" target="_blank" rel="noopener" + >更新的方法</a>吧。</p> + + + + 原来浏览器原生支持JS复制到剪切板 + https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + Wed, 15 Mar 2023 22:39:35 +0000 + + https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + <h2 id="第三方库的痛苦">第三方库的痛苦</h2> +<p>在日常的前端开发中,经常需要将一些数据从网页上复制到剪切板中。而实现复制功能,第一时间想到的就是引入第三方库。</p> +<p>曾经过多不少第三方的剪切板的库,是真的很繁琐,又是创建对象,又是绑定DOM,头都要炸了,就个简单的复制功能,第三方库换来换去地测试&hellip;&hellip;</p> +<p>后来看到了vueuse可以直接用,突然觉得,哇!真棒!</p> +<p>直到有一天,搜到了Clipboard api&hellip;&hellip;</p> +<h2 id="原生支持">原生支持</h2> +<p><em><strong>官方文档</strong></em>:https://developer.mozilla.org/en-US/docs/Web/API/Clipboard</p> +<p>不管是读,还是写,统统搞定!而且都还是异步方法。</p> +<p>比如复制文本到剪切板:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">writeText</span><span class="p">(</span><span class="s2">&#34;&lt;empty clipboard&gt;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>复制canvas到剪切板:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">copyCanvasContentsToClipboard</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">onDone</span><span class="p">,</span> <span class="nx">onError</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toBlob</span><span class="p">((</span><span class="nx">blob</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">[</span><span class="k">new</span> <span class="nx">ClipboardItem</span><span class="p">({</span> <span class="p">[</span><span class="nx">blob</span><span class="p">.</span><span class="nx">type</span><span class="p">]</span><span class="o">:</span> <span class="nx">blob</span> <span class="p">})];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">data</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onDone</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onError</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>读取剪切板的文本:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">txt</span> <span class="o">=</span> <span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">readText</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + 浏览器上ts实现前端直传minio + https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/ + Fri, 17 Feb 2023 13:31:56 +0000 + + https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/ + <h2 id="前言">前言</h2> +<p>前端从后端获取到sts,然后直接minio,极大减少服务端的压力。</p> +<p>当然,肯定会这种疑问,为什么不在后端生成临时签名url,给前端上传/下载呢?问就是业务需要 /狗头&hellip;&hellip;</p> +<h2 id="环境">环境</h2> +<ul> +<li><em>minio</em>: ^7.0.32</li> +<li><em>typescript</em>: 4.9.5</li> +</ul> +<h2 id="浏览器上的坑">浏览器上的坑</h2> +<p>为什么标题上要强调“浏览器”呢?</p> +<p>这是因为官方的minio.js,感觉当前版本并没有考虑浏览器的使用场景,无法直接在浏览器上直传。</p> +<p>所以这里就是推荐一个折中的方案,可以在前端直传。</p> +<p>当然了,如果业务允许,当前版本请直接在后端生成临时签名url吧!</p> +<h2 id="minio直传代码实现">minio直传代码实现</h2> +<h3 id="安装miniojs">安装minio.js</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pnpm add -D minio +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="生成client">生成client</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">Minio</span> <span class="nx">from</span> <span class="s1">&#39;minio&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">minioClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Minio</span><span class="p">.</span><span class="nx">Client</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">region</span><span class="o">:</span> <span class="s1">&#39;cn-north-1&#39;</span><span class="p">,</span> <span class="c1">// region字段,极其有必要加上!我在sts场景,不加region字段就报错权限不够/心累... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">endPoint</span><span class="o">:</span> <span class="s1">&#39;192.168.1.1&#39;</span><span class="p">,</span> <span class="c1">// minio的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">port</span><span class="o">:</span> <span class="mi">9000</span><span class="p">,</span> <span class="c1">// minio端口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">useSSL</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 是否使用ssl +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">accessKey</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">secretKey</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sessionToken</span><span class="p">,</span> <span class="c1">// 可选字段,当为sts时,加入此字段 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="putobject直传方案">putObject直传方案</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">putObject</span><span class="p">(</span><span class="nx">bucketName</span><span class="p">,</span> <span class="nx">objectName</span><span class="p">,</span> <span class="nx">stream</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>官方提供的<code>putObject</code>方法,必填这三个字段,前两个很好理解,主要是第三个<code>stream</code>,需要详细看一下。</p> +<p><code>stream</code>的类型是:<code>string | internal.Readable | Buffer</code></p> +<p><code>string</code>很好搞定,直接<code>putObject(bucketName, 'hello.txt', 'hello,world!')</code>这样子上传文本文件。</p> +<p>但是另外两个类型都是nodejs的啊&hellip;啊这&hellip;(也许是我错了,有大佬能够解决的请务必直接告诉我&hellip;)</p> +<h3 id="折中方案">折中方案</h3> +<p>那么折中的方案就是由前端生成临时签名url,再由前端进行上传 /哭。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">minioClient</span><span class="p">.</span><span class="nx">presignedPutObject</span><span class="p">(</span><span class="nx">bucketName</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后利用http请求url进行上传即可。虽然比较曲折,但是目前相对比较好的前端解决方案。</p> +<h2 id="http请求url上传">http请求url上传</h2> +<p>此处介绍下http请求上传的相关操作。</p> +<h3 id="请求上传">请求上传</h3> +<p>请求上传可以有三种方案。</p> +<h4 id="xmlhttprequest">XMLHttpRequest</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">xhrUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="fetch">Fetch</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fetchUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;PUT&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">body</span><span class="o">:</span> <span class="nx">file</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="axios">Axios</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">axiosUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">file</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="nx">file</span><span class="p">.</span><span class="nx">type</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="promise">Promise</h3> +<p>此处可以封装一下请求,叠加<code>promise</code>buff,此处以<code>XMLHttpRequest</code>为例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">uploadRequest</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">response</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> +</span></span><span class="line"><span class="cl"> <span class="nx">reject</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="事件响应">事件响应</h3> +<p>要完成上传,怎么能没有响应事件呢。</p> +<p>此处的事件包括:</p> +<ul> +<li>上传进度</li> +<li>上传完成</li> +<li>上传失败</li> +</ul> +<p>请求代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">uploadRequest</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">upload</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;progress&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploadProgress</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="p">((</span><span class="nx">e</span><span class="p">.</span><span class="nx">loaded</span> <span class="o">/</span> <span class="nx">e</span><span class="p">.</span><span class="nx">total</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">).</span><span class="nx">toFixed</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> <span class="c1">// 更新进度,此处不保留小数点 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploaded</span><span class="p">()</span> <span class="c1">// 响应上传完成事件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploadErr</span><span class="p">((</span><span class="nx">error</span> <span class="nx">as</span> <span class="nb">Error</span><span class="p">).</span><span class="nx">message</span><span class="p">)</span> <span class="c1">// 响应上传错误,此处ts处理error +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> <span class="nx">onUploadErr</span><span class="p">(</span><span class="sb">`http code is </span><span class="si">${</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> <span class="p">}</span> <span class="c1">// 响应上传错误 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="取消上传">取消上传</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">xhr</span><span class="p">.</span><span class="nx">abort</span><span class="p">()</span> <span class="c1">// 取消上传 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="性能优化">性能优化</h2> +<ul> +<li>对于大文件,或者说需要后台运行的上传任务,可以使用web worker来跑</li> +<li>对于大文件,可以使用切片的方式提高并发,切片的方式也可以实现断点续传,只是需要注意文件切片的顺序和唯一id</li> +</ul> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/minio/minio-js" target="_blank" rel="noopener" + >github.com/minio/minio-js</a></li> +</ul> + + + + WebAssembly -- 未来前端开发的必备技能 + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + Thu, 09 Feb 2023 13:15:34 +0000 + + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + <img src="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/cover.jpg" alt="Featured image of post WebAssembly -- 未来前端开发的必备技能" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<h2 id="快速上手">快速上手</h2> +<h3 id="用go写一个hello-world">用go写一个hello world</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello, WebAssembly!&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="将go文件编译成wasm文件">将go文件编译成wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">GOOS</span><span class="o">=</span>js <span class="nv">GOARCH</span><span class="o">=</span>wasm go build -o static/main.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="拷贝出wasm_execjs">拷贝出wasm_exec.js</h3> +<p>该文件为go的wasm的js支持文件</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cp <span class="s2">&#34;</span><span class="k">$(</span>go env GOROOT<span class="k">)</span><span class="s2">/misc/wasm/wasm_exec.js&#34;</span> static +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="html文件调用wasm文件">html文件调用wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证调用">验证调用</h3> +<p>浏览器加载html文件,f12打开控制台,可以看到wasm的打印消息。</p> +<h2 id="go与js的类型转换">go与js的类型转换</h2> +<h3 id="类型映射">类型映射</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><p>如上为官方给出的go与js的类型映射表。</p> +<p>比如在go中调用js函数,参数为<code>array</code>,那么就可以直接将go的<code>[]interface{}</code>类型的变量作为参数使用。</p> +<h3 id="函数转换数组">函数转换数组</h3> +<p><code>syscall/js</code>提供了两个函数:</p> +<ul> +<li>CopyBytesToGo:<code>func CopyBytesToGo(dst []byte, src Value) int</code></li> +<li>CopyBytesToJS:<code>func CopyBytesToJS(dst Value, src []byte) int</code></li> +</ul> +<p>两者对于go而言,类型都是<code>[]byte</code>,但是对于js而言,需要<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型,否则就会报错。</p> +<p>那么,如何在go中生成一个<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型的变量呢?官方的类型映射表也没有啊&hellip;那么就看下一步。</p> +<h3 id="其余类型">其余类型</h3> +<p>对于非官方类型映射表内的类型,和官方提供的两个数据类型转换之外的类型,可以通过一种通用的方式来生成,以上一步的<code>Uint8Array</code>为例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">length</span><span class="o">&gt;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>实际使用案例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// goData []byte{...} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">jsData</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="nx">len</span><span class="p">(</span><span class="nx">goData</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">CopyBytesToJS</span><span class="p">(</span><span class="nx">jsData</span><span class="p">,</span> <span class="nx">goData</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>那么,比如js中的<code>Date</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">dateConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Date&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">dateConstructor</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s2">&#34;2020-10-01&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="极端情况">极端情况</h3> +<p>好吧,还有最后一个方案,如果遇到极端情况,上述方案都无法解决,那么请转换成字符串吧!让go和js用各自的方法分别处理一波,得到自己想要的结果或者给出各自想给的数据。</p> +<h2 id="js调用go函数">js调用go函数</h2> +<blockquote> +<p>此处需要在go中引入<a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a>,以实现js相关的操作。</p> +</blockquote> +<h3 id="注册go函数">注册go函数</h3> +<p>将go的函数注册为js的函数,由js来进行调用。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleCount</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">count</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Int</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">js</span><span class="p">.</span><span class="nf">ValueOf</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleEvent&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleEvent</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>js.Func()</code> 接受一个函数类型作为其参数,该函数的定义是固定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"><span class="c1">// this 即 JavaScript 中的 this +</span></span></span><span class="line"><span class="cl"><span class="c1">// args 是在 JavaScript 中调用该函数的参数列表。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 返回值需用 js.ValueOf 映射成 JavaScript 的值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>js.ValueOf返回作为js的值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用">js调用</h3> +<p>在js中使用也非常简单,引入wasm文件之后,直接调用函数即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleEvent</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">// 传入参数1 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go调用js函数">go调用js函数</h2> +<p>如果在js中本身已经定义了函数,那么在go中也可以直接调用该函数,进行运算,将得出的结果在go中继续使用。</p> +<h3 id="定义js函数">定义js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="go中调用js函数">go中调用js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;add&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="c1">// 此处输出类型为js.Value,无法直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nf">Int</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// 使用.Int()将其转换为go中的类型,即可直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入wasm">引入wasm</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="结果">结果</h3> +<p>在前端调试台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;number: 3&gt; +</span></span><span class="line"><span class="cl">4 +</span></span></code></pre></td></tr></table> +</div> +</div><p>第一个结果就是js.Value的值,第二个结果则是转换成go的值,并按照逻辑进行了<code>+1</code>处理。</p> +<h2 id="回调函数解决go函数阻塞问题">回调函数/解决go函数阻塞问题</h2> +<blockquote> +<p>The Go function fn is called with the value of JavaScript&rsquo;s &ldquo;this&rdquo; keyword and the arguments of the invocation. The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.</p> +<p>Invoking the wrapped Go function from JavaScript will pause the event loop and spawn a new goroutine. Other wrapped functions which are triggered during a call from Go to JavaScript get executed on the same goroutine.</p> +<p>As a consequence, if one wrapped function blocks, JavaScript&rsquo;s event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.</p> +</blockquote> +<p><code>syscall/js</code>官方文档表明,如果go包装函数阻塞,那么js的事件循环也将被阻塞,直到函数返回,调用任何需要事件循环(如fetch)的异步js api都导致立即死锁。因此,一个阻塞函数应该显式地启动一个新的协程。</p> +<p>此处,可以在go中注册一个回调函数,加上协程实现异步,不会产生堵塞。</p> +<h3 id="注册函数">注册函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">username</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">String</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;hello, %s !&#34;</span><span class="p">,</span> <span class="nx">username</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-1">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleRender</span><span class="p">(</span><span class="s2">&#34;tom&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">hello, tom ! // 隔了3秒之后,输出了回调函数的值 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go实现promise">Go实现Promise</h2> +<p>上一步的回调函数,解决了函数阻塞问题,此处,结合回调函数实现promise,来丰富异步场景。</p> +<p>在js中,promise是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>async</code>和<code>await</code>调用,拿到结果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">printMessage</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">message</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在go中又如何构建promise呢?这里可以用到上述go与js的类型转换,创建一个promise:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="注册函数-1">注册函数</h3> +<p>go的完整实现如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">handler</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="s">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">promiseConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">promiseConstructor</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-2">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">HandleRender</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出-1">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">message: hello, world ! // 隔了3秒之后输出 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="操作dom">操作DOM</h2> +<h3 id="使用document">使用document</h3> +<p>定义一个全局的document</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">var docuemnt = js.Global().Get(&#34;document&#34;) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="获取元素">获取元素</h3> +<p>获取一个<code>id</code>为<code>container</code>的<code>div</code>,设置<code>background-color: red</code>、<code>widht: 600</code>、<code>height: 400</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;container&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElementStyle</span> <span class="p">=</span> <span class="nx">container</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;style&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;background&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="s">&#34;600px&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="s">&#34;400px&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建元素">创建元素</h3> +<p>创建一个<code>id</code>为<code>image</code>的<code>image</code>,设置<code>width:300</code>、<code>height:200</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">imageElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;createElement&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加子元素">添加子元素</h3> +<p>将<code>image</code>添加为<code>id</code>为<code>container</code>的<code>div</code>的子元素</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">containerElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;appendChild&#34;</span><span class="p">,</span> <span class="nx">imageElement</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加事件">添加事件</h3> +<p>给<code>image</code>添加右击事件,右击<code>image</code>则阻止右键菜单</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 定义响应函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">handlePreventEventCallBack</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;preventDefault&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 给image添加事件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;contextmenu&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handlePreventEventCallBack</span><span class="p">))</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里需要注意的是,当不再调用响应事件函数时,必须调用Func.Release以释放资源:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">cb</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Func</span> +</span></span><span class="line"><span class="cl"><span class="nx">cb</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="nx">any</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;button clicked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">cb</span><span class="p">.</span><span class="nf">Release</span><span class="p">()</span> <span class="c1">// 如果不再单击该按钮,则释放该函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;myButton&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;click&#34;</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="canvas">Canvas</h2> +<p>这里放一波canvas的案例,包含了一些常用方法,可以参考完成更多操作。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;math&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">radius</span> <span class="p">=</span> <span class="mi">200</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">canvas</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="nx">height</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getContext&#34;</span><span class="p">,</span> <span class="s">&#34;2d&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;beginPath&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;arc&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="nx">math</span><span class="p">.</span><span class="nx">Pi</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;lightpink&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fill&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;lineWidth&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;strokeStyle&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;stroke&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;font&#34;</span><span class="p">,</span> <span class="s">&#34;20px Comic Sans MS&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;blue&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fillText&#34;</span><span class="p">,</span> <span class="s">&#34;Hello, World !&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="o">-</span><span class="mi">60</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>渲染结果:</p> +<p><img src="https://user-images.githubusercontent.com/25321169/217725808-5dd94da2-bb43-4129-95a9-27f3f85daba5.png" + + + + loading="lazy" + + alt="go_wasm_canvas" + + +></p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a></li> +<li><a class="link" href="https://github.com/golang/go/wiki/WebAssembly" target="_blank" rel="noopener" + >go wiki WebAssembly</a></li> +<li><a class="link" href="https://geektutu.com/post/quick-go-wasm.html" target="_blank" rel="noopener" + >Go WebAssembly (Wasm) 简明教程</a></li> +<li><a class="link" href="https://withblue.ink/2020/10/03/go-webassembly-http-requests-and-promises.html" target="_blank" rel="noopener" + >Go, WebAssembly, HTTP requests and Promises</a></li> +</ul> + + + + wow.js和animate css在vue3中的应用 + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + Sat, 28 Jan 2023 16:09:44 +0000 + + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + <h2 id="环境">环境</h2> +<ul> +<li>vue 3.2</li> +<li>typescript 4.7.4</li> +<li>wow.js 1.2.2</li> +<li>animate.css 4.1.1</li> +</ul> +<h2 id="animatecss">animate.css</h2> +<h3 id="下载">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add animate.css -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;animate.css&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<p>需要注意的是,animate css在4.0之后使用<code>animate__</code>前缀</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="动画延迟">动画延迟</h3> +<h4 id="官方方法">官方方法</h4> +<p>官方给出的动画延迟是<code>animate__delay-2s</code>、<code>animate__delay-3s</code> &hellip;&hellip;</p> +<p>直接在class中添加即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animate__delay-2s&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="自定义延迟">自定义延迟</h4> +<p>特殊场景需要使用不同于官方的延迟时间,因此可以自定义延迟时间,直接声明延迟的类,然后在class上加入即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-1</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">100</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">300</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-3</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">500</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-4</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">700</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-5</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">900</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-2&#34;</span><span class="p">&gt;</span>Another animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="wowjs">wow.js</h2> +<h3 id="下载-1">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add wow.js -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">WOW</span> <span class="nx">from</span> <span class="s1">&#39;wow.js&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">WOW</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">boxClass</span><span class="o">:</span> <span class="s1">&#39;wow&#39;</span><span class="p">,</span> <span class="c1">// 类名,在用户滚动时显示隐藏的框。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">animateClass</span><span class="o">:</span> <span class="s1">&#39;animate__animated&#39;</span><span class="p">,</span> <span class="c1">// 触发CSS动画的类名称 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">offset</span><span class="o">:</span> <span class="mi">300</span><span class="p">,</span> <span class="c1">// 定义浏览器视口底部与隐藏框顶部之间的距离。当用户滚动并到达此距离时,隐藏的框会显示出来。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mobile</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在移动设备上打开/关闭WOW.js。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">live</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在页面上同时检查新的WOW元素。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}).</span><span class="nx">init</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用-1">使用</h3> +<p>使用<code>wow</code>直接替代<code>animate__animated</code>即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;wow animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>由于写一个页面需要使用到wow,好多年没用过了,查了一下文档超多版本教程,使用起来各种不成功,难受&hellip;暂时也没找到可替代的方案&hellip;</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/graingert/wow" target="_blank" rel="noopener" + >wow</a></li> +<li><a class="link" href="https://animate.style/" target="_blank" rel="noopener" + >animate css</a></li> +</ul> + + + + python3 socket tcp example + https://blog.hunterji.com/p/python3-socket-tcp-example/ + Thu, 09 Jun 2022 22:57:54 +0000 + + https://blog.hunterji.com/p/python3-socket-tcp-example/ + <h2 id="socket-tcp-server">socket tcp server</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_tcp_server</span><span class="p">(</span><span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s1">&#39;0.0.0.0&#39;</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">9000</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket tcp 服务端 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务器tcp server接收信息的端口, 默认9000 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param buffer_size: 套接字缓冲区大小, 默认1024 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;服务端开始运行...</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">client</span><span class="p">,</span> <span class="n">sender_info</span> <span class="o">=</span> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">receive_data</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;客户端地址: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sender_info</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;来自客户端的信息: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">receive_data</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1"># 返回消息</span> +</span></span><span class="line"><span class="cl"> <span class="n">client</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;response...&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_tcp_server</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="socket-tcp-client">socket tcp client</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_tcp_client_send_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket tcp 客户端发送消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param message: 消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务端的ip地址 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务端的端口号 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">family</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">response</span> <span class="o">=</span> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;response : </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">decode</span><span class="p">()))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_tcp_client_send_message</span><span class="p">(</span><span class="s1">&#39;hello,world!&#39;</span><span class="p">,</span> <span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">9000</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + python3 socket udp example + https://blog.hunterji.com/p/python3-socket-udp-example/ + Thu, 09 Jun 2022 22:57:54 +0000 + + https://blog.hunterji.com/p/python3-socket-udp-example/ + <h2 id="socket-udp-server">socket udp server</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_udp_server</span><span class="p">(</span><span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s1">&#39;0.0.0.0&#39;</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">9000</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket udp 服务端 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务器udp server接收信息的端口, 默认9000 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param buffer_size: 套接字缓冲区大小, 默认1024 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_DGRAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;服务端开始运行...</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">receive_data</span><span class="p">,</span> <span class="n">sender_info</span> <span class="o">=</span> <span class="n">udp_socket</span><span class="o">.</span><span class="n">recvfrom</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;客户端地址: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sender_info</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;来自客户端的信息: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">receive_data</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_udp_server</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="socket-udp-client">socket udp client</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_udp_client_send_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket udp 客户端发送消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param message: 消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务端的ip地址 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务端的端口号 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_client_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">family</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">SOCK_DGRAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_client_socket</span><span class="o">.</span><span class="n">sendto</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="p">(</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_udp_client_send_message</span><span class="p">(</span><span class="s1">&#39;hello,world!&#39;</span><span class="p">,</span> <span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">9000</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Archives + https://blog.hunterji.com/archives/ + Sun, 06 Mar 2022 00:00:00 +0000 + + https://blog.hunterji.com/archives/ + + + + VUE3+TS+微前端实践 + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + Wed, 16 Feb 2022 15:07:56 +0000 + + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + <h2 id="前言">前言</h2> +<p>基于架构的调整,前端开始转为微前端。经过调研,决定使用<a class="link" href="https://qiankun.umijs.org/zh/guide/getting-started" target="_blank" rel="noopener" + >qiankun</a>微服务框架来使用,本文将介绍VUE3+TS+qiankun的实践经过。微服务架构的优势之一在于可以结合不同技术栈的节点,基于技术栈的考虑,此处用的都是vue3。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>vue 3.0.0</li> +<li>TypeScript 4.1.5</li> +<li>vue router 4.0.0</li> +<li>@vue/cli 4.5.15</li> +<li>qiankun 2.6.3</li> +</ul> +<h2 id="实践">实践</h2> +<h3 id="架构">架构</h3> +<p><img src="https://blog.hunterji.com/../assets/qiankun_example.jpg" + + + + loading="lazy" + + alt="qiankun_example" + + +></p> +<p>如上图所示,微服务架构将会由多个节点构成,首先由一个主节点<code>site_base</code>连接所有子节点,子节点可以不断拓展。</p> +<h3 id="主节点">主节点</h3> +<p>主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base</a></p> +<p>创建主节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_base +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_base +</span></span></code></pre></td></tr></table> +</div> +</div><p>安装<code>qiankun</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add qiankun +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/App.vue</code>中添加路由和渲染节点</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;nav&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/about&#34;</span><span class="p">&gt;</span>About<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site1&#34;</span><span class="p">&gt;</span>Site1<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site2&#34;</span><span class="p">&gt;</span>Site2<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span><span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site1&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site2&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/main.ts</code>中引入子节点配置</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id,对应上一步中src/App.vue中的渲染节点 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9002/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site2&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> <span class="c1">// 注册应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="子节点">子节点</h3> +<p>子节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1</a></p> +<p>此处以<code>site1</code>为例,<code>site2</code>同理。</p> +<p>创建子节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_1 +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_1 +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/App.vue</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/views/Home.vue</code>,修改其内容,写一点标识性的文本</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Hello, Site1!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/pulic-path.ts</code>,第一行的注视一定要加,避免eslint对于变量的报错</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="cm">/* eslint-disable camelcase */</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">((</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">__webpack_public_path__</span> <span class="o">=</span> <span class="p">(</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__INJECTED_PUBLIC_PATH_BY_QIANKUN__</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/router/index.ts</code>,此处直接返回<code>routes</code>,而不是<code>router</code>,并且修改</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">RouteRecordRaw</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">RouteRecordRaw</span><span class="p">&gt;</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Home&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/Home.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/about&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;About&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/About.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 直接返回routes,由其它地方处理创建路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">routes</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;./public-path&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createRouter</span><span class="p">,</span> <span class="nx">createWebHistory</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">routes</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">instance</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">history</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">render</span> <span class="p">(</span><span class="nx">props</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="p">{</span> <span class="nx">container</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">props</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当为微服务主节点情况下访问,会设置二级路径,而直接访问时没有二级路径,此处需要根据实际情况修改 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">history</span> <span class="o">=</span> <span class="nx">createWebHistory</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span> <span class="o">?</span> <span class="s1">&#39;/site1&#39;</span> <span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="nx">createRouter</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">routes</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">mount</span><span class="p">(</span><span class="nx">container</span> <span class="o">?</span> <span class="nx">container</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> <span class="o">:</span> <span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">bootstrap</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;%c &#39;</span><span class="p">,</span> <span class="s1">&#39;color: green &#39;</span><span class="p">,</span> <span class="s1">&#39;vue3.0 app bootstraped&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">storeTest</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="k">void</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`[onGlobalStateChange - </span><span class="si">${</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">]:`</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ignore</span>: <span class="kt">props.name</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span>: <span class="kt">props.name</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">mount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">storeTest</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$onGlobalStateChange</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$setGlobalState</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">unmount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">unmount</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">_container</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">.</span><span class="nx">destroy</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>vue.config.js</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">name</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./package&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">resolve</span> <span class="p">(</span><span class="nx">dir</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="nx">dir</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">port</span> <span class="o">=</span> <span class="mi">9001</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputDir</span><span class="o">:</span> <span class="s1">&#39;dist&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assetsDir</span><span class="o">:</span> <span class="s1">&#39;static&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filenameHashing</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">devServer</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">hot</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">disableHostCheck</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">overlay</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">warnings</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">errors</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Access-Control-Allow-Origin&#39;</span><span class="o">:</span> <span class="s1">&#39;*&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 自定义webpack配置 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">configureWebpack</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alias</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;@&#39;</span><span class="o">:</span> <span class="nx">resolve</span><span class="p">(</span><span class="s1">&#39;src&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">output</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 把子应用打包成 umd 库格式 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">library</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">-[name]`</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">libraryTarget</span><span class="o">:</span> <span class="s1">&#39;umd&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonpFunction</span><span class="o">:</span> <span class="sb">`webpackJsonp_</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证">验证</h3> +<p>主节点和子节点分别独立运行,但是子节点的地址需要跟主节点配置中子节点对应的地址相同。</p> +<p>在主节点上点击子节点的路由,即可在主节点上访问子节点的页面了!</p> +<img src="../assets/qiangkun_example_result.png" alt="qiangkun_example_result" style="zoom:50%;" /> +<h2 id="主节点优化">主节点优化</h2> +<p>主节点除了如上配置,可以进行两项优化:</p> +<ul> +<li>模块化子节点配置</li> +<li>添加过渡状态,当加载子节点时窗口顶部出现加载进度条</li> +</ul> +<p>优化后主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize</a></p> +<h3 id="模块化子节点配置">模块化子节点配置</h3> +<p>创建文件夹<code>src/childNodes</code>,然后创建文件<code>src/childNodes/apps.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">apps</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">start</span> <span class="kr">from</span> <span class="s1">&#39;./childNodes&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="过渡效果">过渡效果</h3> +<p>此处的过渡效果采用<code>NProgress</code>库,先来安装一波</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add nprogress +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addGlobalUncaughtErrorHandler</span><span class="p">,</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">NProgress</span> <span class="kr">from</span> <span class="s1">&#39;nprogress&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;nprogress/nprogress.css&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点加载前 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">beforeLoad</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开始进度条 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点挂载后 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">afterMount</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">done</span><span class="p">()</span> <span class="c1">// 进度条结束 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p><code>qiankun</code>框架确实挺不错的,配置也并不是复杂,但是唯一想吐槽的一点是对于ts的支持感觉不太好/狗头,或许是我写得不够好吧,后面会持续优化使用。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://qiankun.umijs.org/zh/guide" target="_blank" rel="noopener" + >qiankun官方文档</a></li> +<li><a class="link" href="https://juejin.cn/post/6981656757458173988" target="_blank" rel="noopener" + >vue3+ts+qiankun的微前端快速上手</a></li> +</ul> + + + + VUE项目国际化 + https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/ + Wed, 29 Dec 2021 14:24:30 +0000 + + https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/ + <h2 id="前言">前言</h2> +<p><code>i18n</code>是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。 在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。</p> +<p>Node.js本身有一个<code>i18n</code>的包,但是为了更好地结合vue,此处我们使用的是<code>vue-i18n</code>。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Vue 3.0.0</li> +<li>TypeScript 4.5.4</li> +<li>Vue Cli 4.5.15</li> +</ul> +<h2 id="安装">安装</h2> +<p>使用vue cli安装,会自动生成文件且引入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue add i18n +</span></span></code></pre></td></tr></table> +</div> +</div><p>执行过程中将需要填写如下问题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">locale</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="w"> </span><span class="n">localization</span><span class="p">.</span><span class="w"> </span><span class="p">[</span><span class="err">默认选项</span><span class="p">]</span><span class="n">en</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">fallback</span><span class="w"> </span><span class="n">locale</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="w"> </span><span class="n">localization</span><span class="p">.</span><span class="w"> </span><span class="p">[</span><span class="err">默认选项</span><span class="p">]</span><span class="n">en</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">directory</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">store</span><span class="w"> </span><span class="n">localization</span><span class="w"> </span><span class="n">messages</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="p">.</span><span class="w"> </span><span class="n">It</span><span class="s1">&#39;s stored under `src` directory. [默认选项]locales +</span></span></span><span class="line"><span class="cl"><span class="s1">? Enable legacy API (compatible vue-i18n@v8.x) mode ? [默认选项]No +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>执行完成之后,将会自动处理如下文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// vue add i18n执行结束后的git status +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">new file: .env // 执行过程中的第一个和第二个问题将使用环境变量更新 +</span></span><span class="line"><span class="cl">modified: package.json +</span></span><span class="line"><span class="cl">new file: src/components/HelloI18n.vue // 官方给出的demo组件 +</span></span><span class="line"><span class="cl">new file: src/i18n.ts // 自动读取多语言的json文件且创建i18n实例 +</span></span><span class="line"><span class="cl">new file: src/locales/en.json // 多语言json文件所在文件夹,默认选择的en语言,所以生成一个默认的语言json文件 +</span></span><span class="line"><span class="cl">modified: src/main.ts // 引入i18n +</span></span><span class="line"><span class="cl">new file: vue.config.js // 配置i18n +</span></span><span class="line"><span class="cl">modified: yarn.lock +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多语言配置">多语言配置</h2> +<p>想要多语言则需要配置多个对应语言的json文件,其字段必须相同,否则当用户切换时,会出现找不到该字段对应文字的问题。</p> +<p>为了便于切换和处理,此处不再使用<code>en</code>之类的简写,而是和系统语言名称对应起来,此处将使用<code>zh_CN</code>和<code>en_GB</code>来做配置。</p> +<h3 id="创建多语言json文件">创建多语言json文件</h3> +<p>此处生成两个文件<code>src/locales/zh_CN.json</code>和<code>src/locales/en_GB.json</code>,内容分别如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;中文&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;你好,世界!&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;English&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello, World !&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="更新语言标识">更新语言标识</h3> +<p>将相关文件的<code>en</code>都改成<code>en_GB</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">##env +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">VUE_APP_I18N_LOCALE=en_GB +</span></span><span class="line"><span class="cl">VUE_APP_I18N_FALLBACK_LOCALE=en_GB +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/i18n.ts +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">createI18n</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">legacy</span>: <span class="kt">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span>: <span class="kt">process.env.VUE_APP_I18N_LOCALE</span> <span class="o">||</span> <span class="s1">&#39;en_GB&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fallbackLocale</span>: <span class="kt">process.env.VUE_APP_I18N_FALLBACK_LOCALE</span> <span class="o">||</span> <span class="s1">&#39;en_GB&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">messages</span>: <span class="kt">loadLocaleMessages</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<h3 id="重写首页">重写首页</h3> +<p>此处重写<code>src/views/Home.vue</code>文件,先按照正常的内容去写,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nx">Hello</span><span class="p">,</span> <span class="nx">World</span> <span class="o">!</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置多语言">配置多语言</h3> +<p>需要配置多语言的地方就是展示的文字,所以将该文字替换掉即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useI18n</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue-i18n&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">t</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useI18n</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处引入<code>t</code>方法,而其参数<code>message</code>为上面配置的每一个json文件中的<code>message</code>字段,其将用json对应字段的value来展示。</p> +<p>可能有小伙伴儿要问,那如果不是html中的展示文字怎么办呢?</p> +<p>这也一样使用<code>t</code>方法的,示例如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="na">:placeholder</span><span class="o">=</span><span class="s">&#34;t(&#39;message&#39;)&#34;</span> <span class="p">/&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useI18n</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue-i18n&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">t</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useI18n</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">exampleVal</span> <span class="o">=</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处我们的json结构都是单层的,如果是嵌套的结构的话,可以使用<code>t('home.message')</code>这样的方式来引用。</p> +<h3 id="切换语言">切换语言</h3> +<p>此处需要引入<code>locale</code>,主要通过更新locale.value来切换语言。此处的切换是全局的,所以只需要写一个切换组件,其它组件都将会被切换语言。</p> +<p>写一个下拉框来实现语言的切换,完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">select</span> <span class="nt">v-model</span><span class="s">=&#34;state.language&#34; @change=&#34;handleLanguageChange&#34;&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;zh_CN&#34;&gt;zh_CN&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;en_GB&#34;&gt;en_GB&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/select&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/div&gt; +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;/template&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;script setup lang=&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">reactive</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">useI18n</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="nt">-i18n</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="p">{</span> <span class="na">t</span><span class="err">,</span> <span class="na">locale</span> <span class="p">}</span> <span class="err">=</span> <span class="na">useI18n</span><span class="err">()</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">state</span><span class="o">:</span> <span class="p">{</span> <span class="na">language</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="err">=</span> <span class="na">reactive</span><span class="err">(</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="na">language</span><span class="o">:</span> <span class="err">&#39;</span><span class="na">en_GB</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">)</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">handleLanguageChange </span><span class="o">=</span> <span class="err">(</span><span class="na">option</span><span class="o">:</span> <span class="p">{</span> <span class="na">target</span><span class="o">:</span> <span class="p">{</span> <span class="na">value</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="p">}</span><span class="err">)</span> <span class="err">=</span><span class="p">&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 主要通过更新locale.value来切换语言 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">option</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>通过下拉框切换选项可以切换语言。</p> +<h3 id="本地环境">本地环境</h3> +<p>虽然<code>i18n</code>设置了默认的语言,但是友好的交互应当是根据用户的环境语言加载。这里需要使用<code>navigator.language</code>来拿到用户的环境语言,通过判断环境语言切换初始的语言配置。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">language</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;zh-CN&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;en-GB&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>完整代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">select</span> <span class="nt">v-model</span><span class="s">=&#34;state.language&#34; @change=&#34;handleLanguageChange&#34;&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;zh_CN&#34;&gt;zh_CN&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;en_GB&#34;&gt;en_GB&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/select&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/div&gt; +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;/template&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;script setup lang=&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">reactive</span><span class="err">,</span> <span class="na">onMounted</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">useI18n</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="nt">-i18n</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="p">{</span> <span class="na">t</span><span class="err">,</span> <span class="na">locale</span> <span class="p">}</span> <span class="err">=</span> <span class="na">useI18n</span><span class="err">()</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">state</span><span class="o">:</span> <span class="p">{</span> <span class="na">language</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="err">=</span> <span class="na">reactive</span><span class="err">(</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="na">language</span><span class="o">:</span> <span class="err">&#39;</span><span class="na">en_GB</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">)</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">handleLanguageChange </span><span class="o">=</span> <span class="err">(</span><span class="na">option</span><span class="o">:</span> <span class="p">{</span> <span class="na">target</span><span class="o">:</span> <span class="p">{</span> <span class="na">value</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="p">}</span><span class="err">)</span> <span class="err">=</span><span class="p">&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 主要通过更新locale.value来切换语言 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">option</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fetchData</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">language</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 环境语言中间的线是居中的 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">case</span> <span class="s1">&#39;zh-CN&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;en-GB&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">onMounted</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fetchData</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>运行之后,默认语言不再是英语,而是跟环境语言一致的。</p> +<h3 id="参考文档">参考文档</h3> +<ul> +<li><a class="link" href="https://kazupon.github.io/vue-i18n/zh/started.html" target="_blank" rel="noopener" + >vue-i18n</a></li> +</ul> + + + + electron的__dirname not defined报错解决 + https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:29:14 +0000 + + https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue cli plugin electron builder开发项目,用到涉及node相关的功能,比如<code>fs</code>、<code>path</code>,出现报错<code>__dirname not defined </code>。</p> +<h2 id="解决">解决</h2> +<p>该问题在于需要开启electron对于node操作的支持。</p> +<h3 id="安装typesnode">安装@types/node</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add @types/node -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="修改配置">修改配置</h3> +<p>修改electron的配置文件,此处我的配置文件为<code>src/background.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegrationInWorker</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue3+ts+electron不支持require is not defined报错解决 + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:23:46 +0000 + + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue3+typescript+electron开发时,遇到一个报错为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Uncaught ReferenceError: require is not defined +</span></span></code></pre></td></tr></table> +</div> +</div><p>点进去是<code>module.exports = require(&quot;events&quot;)</code>,并不是自己的代码中的<code>require</code>,因此无法改变写法只能让项目去支持它。</p> +<h2 id="解决">解决</h2> +<p>在electron的配置文件中,新增或者修改如下配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">contextIsolation</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/64706829/electron-tedious-requireevents-is-not-defined" target="_blank" rel="noopener" + >Electron/Tedious: require(&ldquo;events&rdquo;) is not defined</a></li> +</ul> + + + + uniapp canvas生成海报功能拆解和问题记录 + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + Thu, 25 Nov 2021 17:07:55 +0000 + + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + <h2 id="前言">前言</h2> +<p>最近在用uniapp开发小程序,需要用到canvas画海报然后再保存本地。</p> +<p>之前写过同样功能的<a class="link" href="https://github.com/Kuari/Blog/issues/1" target="_blank" rel="noopener" + >文章</a>,不过场景不同,之前是在web上生成海报,该场景可以使用之前文章的方法——html转canvas来实现。</p> +<p>但是uniapp则不同,该框架是去DOM化的,因此只能使用uniapp的官方canvas来实现。</p> +<h2 id="功能拆解">功能拆解</h2> +<p>网上找到的文章,有几篇写得挺好的,展现了完整的功能。这里我把用到的几个功能拆解出来,而不用先通读整篇代码。</p> +<h3 id="创建canvas">创建canvas</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">canvas</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width: 300px; height: 200px;&#34;</span> <span class="na">canvas-id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">canvas</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onReady</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="背景色">背景色</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;red&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 此处其实绘制了一个300x200的红色矩形,但是其大小跟canvas大小相同即为背景色了 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="加载图片">加载图片</h3> +<h4 id="1本地图片">1)本地图片</h4> +<p>图片直接在项目中,可以直接加载。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="s2">&#34;./background.png&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="2url">2)url</h4> +<p>此处url返回的为文件流,在uniapp中无法直接加载,需要转换成本地信息才可以使用。</p> +<p>有两种方式可以使用,小程序都需要添加download合法域名:</p> +<ul> +<li><em><strong>uni.getImageInfo</strong></em>:获取文件信息,我使用的这个方法</li> +<li><em><strong>uni.downloadFile</strong></em>:下载文件</li> +</ul> +<p>原生使用方法是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>由于js的异步问题,如果图片较大或者多个图片的情况下,会有这边还没加载完,canvas就已经绘制完了的情况,所以这里将其优化下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 首先封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">url</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3base64">3)base64</h4> +<p>如果你的图片数据是base64的,那恭喜你,依然加载不了。当然这存在的情况是,微信开发工具是没有问题的,但是上了真机之后直接无法加载了,这波是小程序的锅。</p> +<p>这里呢需要将图片存储然后用本地地址绘制。</p> +<p>原生方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>优化一波:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getBase64ImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">base64Data</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="nx">base64Data</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getBase64ImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="文字">文字</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFontSize</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">font</span> <span class="o">=</span> <span class="s2">&#34;nomarl bold 13px Arial,sans-serif&#34;</span> <span class="c1">// 加粗等功能 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillText</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="圆角矩形">圆角矩形</h3> +<p>想要绘制一个圆角的矩形,啊&hellip;&hellip;这波就复杂了,原理就不细讲了,直接上代码,调用即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">roundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">width</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">height</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">drawRoundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">strokeStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">fillStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">roundedRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">strokeStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">fillStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">drawRoundedRect</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">86</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="图片加载为圆形">图片加载为圆形</h3> +<p>基本原理是,正常加载图片,canvas画个圆给它裁剪掉,上代码!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 如果小伙伴儿调试时候感觉圆形和图片有点错位,可以开启下面两行注释代码,给圆圈加个边框 +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.setStrokeStyle(&#39;#AAAAAA&#39;) +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.stroke() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">clip</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">image</span><span class="o">-</span><span class="nx">path</span><span class="o">&gt;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">restore</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas生成的海报下载">canvas生成的海报下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">savePoster</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;确定保存到相册吗&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">canvasToTempFilePath</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvasId</span><span class="o">:</span> <span class="s1">&#39;sharePoster&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">saveImageToPhotosAlbum</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">tempFilePath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 此处为执行成功 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">openSetting</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">authSetting</span><span class="p">[</span><span class="s1">&#39;scope.writePhotosAlbum&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限成功,再次点击图片即可保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限失败,无法保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="k">this</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="问题">问题</h2> +<h3 id="图片有时显示有时不显示">图片有时显示有时不显示</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.加载图片”优化代码处,将加载图片全部写成同步的,在开始绘制前将图片全都加载好。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="base64数据的图片在小程序开发工具显示到了真机就不显示了">base64数据的图片在小程序开发工具显示,到了真机就不显示了</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.3)base64”优化代码处,使用该方法即可。小程序canvas无法直接加载base64图片。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas整体画成圆角的">canvas整体画成圆角的</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">canvas背景是透明色,只要画个大小覆盖canvas的圆角矩形或者使用圆角背景图即可。 +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue禁止遮罩层下的页面滚动 + https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/ + Thu, 25 Nov 2021 16:13:10 +0000 + + https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/ + <h2 id="问题">问题</h2> +<p>功能开发过程中写遮罩时,遇到遮罩下页面还可以滚动的问题。</p> +<h2 id="解决">解决</h2> +<p>直接给遮罩下的元素套上一个样式,使其不可滚动。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">:class</span><span class="o">=</span><span class="s">&#34;isPopup ? &#39;disableRoll&#39; : &#39;&#39;&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> ... +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">isPopup</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nc">disableRoll</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">overflow</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue3 script setup响应式初体验 + https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/ + Thu, 04 Nov 2021 20:48:09 +0000 + + https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/ + <h2 id="前言">前言</h2> +<p>最近空下来,正好找个项目尝鲜,把vue3+ts+setup哐哐全堆上,试试最新的前端技术。</p> +<p>从最先体会到的变化,就是关于响应式APIs了。遇到不好问题,怪我没有理解文档/狗头。比如说:</p> +<ul> +<li>明明这个数据改了,怎么没渲染出来?</li> +<li>同样是Arrary,怎么套了个reactive就类型不一样了?</li> +</ul> +<p>所以这里基于遇到的几个问题,来写个笔记。</p> +<h2 id="简单对比">简单对比</h2> +<ul> +<li><a class="link" href="https://v3.cn.vuejs.org/api/sfc-script-setup.html#%E5%93%8D%E5%BA%94%E5%BC%8F" target="_blank" rel="noopener" + >响应式官方文档</a></li> +</ul> +<p>官方案例如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ref</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="nt">@click</span><span class="s">=&#34;count++&#34;</span><span class="p">&gt;{{</span> <span class="na">count</span> <span class="p">}}&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们可以先看看,如果是vue2,怎么做呢?</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">count</span><span class="o">:</span> <span class="mi">0</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="nt">@click</span><span class="s">=&#34;count++&#34;</span><span class="p">&gt;{{</span> <span class="na">count</span> <span class="p">}}&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以很明显地看到此处的<code>count</code>跟上面的官方文档不同,使用了<code>ref</code>方法。这就是setup中的响应式APIs,需要预先声明响应式变量。</p> +<p>如果,不声明呢?那就是直接写成如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>运行一下,首先你会发现,没有任何报错,代码正常运行。但是当你在浏览器上查看,开始改变<code>count</code>的值时,就会发现,怎么页面没有变化?</p> +<p>所以,需要手动命名响应,才会在值变化时触发视图渲染。</p> +<h2 id="ref">ref</h2> +<p>现在来详细讲讲<code>ref</code>,该方法常用于单个变量,比如:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ref</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;hello&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里需要说明一个问题,那就是<code>ref(&quot;hello&quot;) !== &quot;hello&quot;</code>。这就是我说的,为什么同样的值,类型就不同了。那是因为ref返回的是一个<code>Proxy</code>,而非原来的值。在视图中可直接使用,但是在js/ts中操作,需要使用<code>.value</code>来操作,如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;kuari&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">changeName</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;tom&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="reactive">reactive</h2> +<p><code>reactive</code>不同于<code>ref</code>的点在于,其是“深层”的——它影响所有嵌套 property。也就是说,其可用在对象或者数组上。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">form</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">desc</span><span class="o">:</span> <span class="s2">&#34;developer&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>其返回类型也是<code>Proxy</code>,不同点在于,可以直接修改某一个元素的,如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">form</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="s2">&#34;tom&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>但是如果你想整个替换就会报错了。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">form</span> <span class="o">=</span> <span class="p">{...}</span> <span class="c1">// 报错,类型不同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>当使用的是数组时,如果想整个替换,可以将其写成对象,代码如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">selected</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">arr</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;vue&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span>: <span class="kt">0</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;typescript&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span>: <span class="kt">1</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">selected</span><span class="p">.</span><span class="nx">arr</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 整个替换 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">selected</span><span class="p">.</span><span class="nx">arr</span> <span class="o">=</span> <span class="p">[...]</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>关于<code>reactive</code>跟<code>ref</code>一起使用,<code>reactive</code> 将解包所有深层的<code>ref</code>,同时维持 ref 的响应性。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> <span class="nx">count</span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// ref 会被解包 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span> <span class="o">===</span> <span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 它会更新 `obj.count` +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="o">++</span> +</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// 2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="p">)</span> <span class="c1">// 2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 它也会更新 `count` ref +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="o">++</span> +</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="p">)</span> <span class="c1">// 3 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// 3 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="总结">总结</h2> +<p><code>setup</code>总体来说,用起来真的会更加简洁,而响应式虽然好像比之前麻烦些了,但是一定层面上让开发对对于程序有了更深入的操控。墙裂推荐一波!后面再来详细讲讲对于新特性的体验。</p> + + + + 你是个成熟的代码要学会自己按需引入了 + https://blog.hunterji.com/p/%E4%BD%A0%E6%98%AF%E4%B8%AA%E6%88%90%E7%86%9F%E7%9A%84%E4%BB%A3%E7%A0%81%E8%A6%81%E5%AD%A6%E4%BC%9A%E8%87%AA%E5%B7%B1%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5%E4%BA%86/ + Wed, 27 Oct 2021 15:28:00 +0000 + + https://blog.hunterji.com/p/%E4%BD%A0%E6%98%AF%E4%B8%AA%E6%88%90%E7%86%9F%E7%9A%84%E4%BB%A3%E7%A0%81%E8%A6%81%E5%AD%A6%E4%BC%9A%E8%87%AA%E5%B7%B1%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5%E4%BA%86/ + <h2 id="前言">前言</h2> +<p>前端小伙伴儿们是不是经常遇到ui组件全局引入导致体积太大,按需引入导致不断手写会很麻烦。所以,当当!今天我们来让代码自己按需引入,解放前端小伙伴儿们的生产力,早日实现下班自由!(甲方:我要再改十个需求!)</p> +<h2 id="介绍">介绍</h2> +<p>我们这里介绍的是<code>unplugin-vue-components</code>。该组件是由vue核心开发成员antfu开发的,尤大也是推荐的,且是该项目的金牌赞助商。</p> +<p>该组件主要是为了实现vue项目的组件自动引入。</p> +<p>官方文档:<a class="link" href="https://github.com/antfu/unplugin-vue-components" target="_blank" rel="noopener" + >antfu/unplugin-vue-components</a></p> +<h2 id="完整案例">完整案例</h2> +<p>此处我们以vite为例,来主要看一下其对于自定义组件和UI库组件的自动按需引入。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/unplugin_auto_import" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/unplugin_auto_import</a></p> +<h3 id="创建项目">创建项目</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite unplugin_auto_import --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后进入文件夹安装依赖。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> unplugin_auto_import +</span></span><span class="line"><span class="cl">yarn install +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="安装unplugin-vue-components">安装unplugin-vue-components</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D unplugin-vue-components +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置viteconfigjs">配置vite.config.js</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Components</span> <span class="nx">from</span> <span class="s1">&#39;unplugin-vue-components/vite&#39;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="nx">vue</span><span class="p">(),</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Components</span><span class="p">({</span> <span class="cm">/* options */</span> <span class="p">})</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自动引入自定义组件">自动引入自定义组件</h3> +<p>我们默认模板创建的项目中,默认在<code>App.vue</code>中引入了<code>./components/HelloWorld.vue</code>。此处就可以来尝试下如何自动引入了。</p> +<p>在配置了<code>unplugin-vue-components</code>之后,现在只需要删除引入行(其实这时候打开vscode就会发现改行已经灰掉了),被删除行如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">HelloWorld</span> <span class="nx">from</span> <span class="s1">&#39;./components/HelloWorld.vue&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>删除以后完整<code>App.vue</code>如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Vue logo&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./assets/logo.png&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hello Vue 3 + Vite&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="err">#</span><span class="nx">app</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">font</span><span class="o">-</span><span class="nx">family</span><span class="o">:</span> <span class="nx">Avenir</span><span class="p">,</span> <span class="nx">Helvetica</span><span class="p">,</span> <span class="nx">Arial</span><span class="p">,</span> <span class="nx">sans</span><span class="o">-</span><span class="nx">serif</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="o">-</span><span class="nx">webkit</span><span class="o">-</span><span class="nx">font</span><span class="o">-</span><span class="nx">smoothing</span><span class="o">:</span> <span class="nx">antialiased</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="o">-</span><span class="nx">moz</span><span class="o">-</span><span class="nx">osx</span><span class="o">-</span><span class="nx">font</span><span class="o">-</span><span class="nx">smoothing</span><span class="o">:</span> <span class="nx">grayscale</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">-</span><span class="nx">align</span><span class="o">:</span> <span class="nx">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">color</span><span class="o">:</span> <span class="err">#</span><span class="mi">2</span><span class="nx">c3e50</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">margin</span><span class="o">-</span><span class="nx">top</span><span class="o">:</span> <span class="mi">60</span><span class="nx">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现在在命令行中运行<code>yarn dev</code>,再打开浏览器查看<code>http://localhost:3000</code>页面,是不是发现,哎?!居然引入了!(心中狂喜,要早日实现下班自由了)</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gvtvbpyfmmj31bc0u0gn2.jpg" alt="截屏2021-10-27 下午2.47.55" style="zoom:50%;" /> +<h3 id="自动引入ui库组件">自动引入UI库组件</h3> +<p>这里以element plus为例。</p> +<p>首先是安装<code>element plus</code>。官方文档也没说要装,我还以为内置呢,一直报错,有点懵逼,哈哈。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D element-plus +</span></span></code></pre></td></tr></table> +</div> +</div><p>引入element plus的resolver,此处编辑vite.config.js文件,修改后如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Components</span> <span class="nx">from</span> <span class="s1">&#39;unplugin-vue-components/vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ElementPlusResolver</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;unplugin-vue-components/resolvers&#39;</span> <span class="c1">// 引入ElementPlusResolver +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="nx">vue</span><span class="p">(),</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Components</span><span class="p">({</span><span class="nx">resolvers</span><span class="o">:</span> <span class="p">[</span><span class="nx">ElementPlusResolver</span><span class="p">()]})</span> <span class="c1">// 添加配置 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>那么现在神奇的事情来了,就可以直接使用UI库的组件了!</p> +<p>我们在<code>App.vue</code>中使用一个<code>el-button</code>组件试试。我们在<code>App.vue</code>中加入如下行:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">el-button</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span><span class="nx">Kuari</span><span class="o">&lt;</span><span class="err">/el-button&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>添加后<code>App.vue</code>上下代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Vue logo&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./assets/logo.png&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hello Vue 3 + Vite&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">el-button</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span><span class="nx">Kuari</span><span class="o">&lt;</span><span class="err">/el-button&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现在,运行<code>yarn dev</code>,打开浏览器,可以看到,就直接可以使用UI库的组件了。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gvtvnf0wwij31bc0u0ta7.jpg" alt="截屏2021-10-27 下午2.59.06" style="zoom:50%;" /> +<h3 id="打包">打包</h3> +<p>按需引入的功能并不是仅仅在开发时候的,在打包时,该组件也是Tree-shakable的,只会将你用了的组件打包。</p> +<p>按照当前教程所写的项目,当全局引入的时候,打包的dist文件夹为1.1MB,而使用该组件之后打包,其dist文件夹为202KB。</p> +<h2 id="最后">最后</h2> +<p>手动按需导入是不可能手动按需导入的,这辈子都不可能了/狗头。</p> +<p>毕竟是大佬开发的强力工具,希望前端小伙伴儿们早日实现下班自由。</p> + + + + Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + Mon, 25 Oct 2021 16:07:10 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + <h2 id="简介">简介</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a>中,我们了解了如何使用<code>Vite</code>和<code>Electron</code>来快速构建一个Vue3桌面应用。但是,之前构建的应用仅仅是一个简单的版本。在开发过程中,为了更好的开发体验,在开发electron的时候,肯定也希望能有动态模块热重载(HMR),更别说vite那迅雷不及掩耳盗铃儿响叮当之势的加载速度。</p> +<p>因此,接着上一篇文章所完成的项目代码,我们来完成<code>Vite</code>和<code>Electron</code>开发时的动态模块热重载功能。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,可利用electron中的<code>mainWindow.loadURL(&lt;your-url&gt;)</code>来实现。</p> +<p>对于动态模块热重载功能来说,无论是webpack还是vite,其都是将构建内容存入内存,因此我们无法使用<code>mainWindow.loadFile('dist/index.html')</code>这样加载文件的方式。</p> +<p>但是,单纯地改变该配置也是不行的,需要使用vite将开发服务器运行起来,可以正常运行动态模块热重载,而electron直接加载其开发服务器可访问的url,即<code>http://localhost:3000</code>。</p> +<h2 id="实现步骤">实现步骤</h2> +<h3 id="编辑mainjs">编辑main.js</h3> +<p>将<code>mainWindow.loadFile('dist/index.html')</code>更新为<code>mainWindow.loadURL(&quot;http://localhost:3000&quot;)</code>,更新后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span><span class="s2">&#34;http://localhost:3000&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑viteconfigjs">编辑vite.config.js</h3> +<p>修改文件<code>vite.config.js</code>的<code>base</code>,修改后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// vite.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="s2">&#34;./&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="同时开启vite和electron服务">同时开启vite和electron服务</h3> +<p>为了使vite和electron正常运行,需要先运行vite,使得其开发服务器的url可以正常访问,然后再开启electron去加载url。</p> +<p>此处需要安装两个库:</p> +<ul> +<li><strong>concurrently</strong>:阻塞运行多个命令,<code>-k</code>参数用来清除其它已经存在或者挂掉的进程</li> +<li><strong>wait-on</strong>:等待资源,此处用来等待url可访问</li> +</ul> +<p>首先来安装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D concurrently wait-on +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着更新文件<code>package.json</code>,<code>scripts</code>新增两条命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"> <span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>现已添加两条命令:</p> +<ul> +<li><code>yarn electron</code>为等待tcp协议3000端口可访问,然后执行electron</li> +<li><code>yarn electron:serve</code>为阻塞执行开发服务器运行和<code>yarn electron</code>命令</li> +</ul> +<p>运行项目只要执行命令<code>yarn electron:serve</code>即可,当修改项目文件时,桌面应用也将自动更新。</p> +<h2 id="参考文件">参考文件</h2> +<ul> +<li><a class="link" href="https://cn.vitejs.dev/guide/why.html#slow-server-start" target="_blank" rel="noopener" + >为什么选vite</a></li> +<li><a class="link" href="https://dev.to/brojenuel/vite-vue-3-electron-5h4o" target="_blank" rel="noopener" + >vite+vue3+electron+typescript</a></li> +</ul> + + + + Golang如何使用validator校验参数 + https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/ + Sat, 23 Oct 2021 15:31:19 +0000 + + https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/ + <h2 id="简介">简介</h2> +<p>关于测试工程师,有一个笑话,是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯咖啡 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了0.7杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了-1杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了2^32杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯洗脚水 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯蜥蜴 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一份asdfQwer@24dg!&amp;*(@ +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,什么也没要 +</span></span><span class="line"><span class="cl">一个测试工程师走进家酒吧,又走出去又从窗户进来又从后门出去从下水道钻进来 +</span></span><span class="line"><span class="cl">一个测试工程师走进家酒吧,又走出去又进来又出去又进来又出去,最后在外面把老板打了一顿 +</span></span><span class="line"><span class="cl">一个测试工程师走进一 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯烫烫烫的锟斤拷 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了NaN杯Null +</span></span><span class="line"><span class="cl">1T测试工程师冲进一家酒吧,要了500T啤酒咖啡洗脚水野猫狼牙棒奶茶 +</span></span><span class="line"><span class="cl">1T测试工程师把酒吧拆了 +</span></span><span class="line"><span class="cl">一个测试工程师化装成老板走进一家酒吧,要了500杯啤酒,并且不付钱 +</span></span><span class="line"><span class="cl">一万个测试工程师在酒吧外呼啸而过 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯啤酒‘;DROPTABLE酒吧 +</span></span><span class="line"><span class="cl">测试工程师们满意地离开了酒吧 +</span></span></code></pre></td></tr></table> +</div> +</div><p>这个笑话估计也只有开发才明白其中的笑点与心酸吧。</p> +<p>对于一些刚入门的开发来说,这简直就是噩梦。当初刚入门的时候我的代码也是很多毛病,经不起这样的测试,后来渐渐地经验多了后,代码的健壮性逐渐提升,也明白其中比较重要的就是参数的校验。</p> +<p>参数的使用有通过协议的接口调用(如http、rpc&hellip;&hellip;)、函数调用、库调用等等方式。</p> +<p>其实对于http api的请求来说,现在很多web框架都已经自带了参数校验的功能,基本用起来都挺爽的,也无需多讲。</p> +<p>而对于函数调用这样的常见方式,很多是要靠开发自己去校验参数的。如果仅仅是靠注释,在团队开发过程中,难免会有问题产生。起码我觉得,永远不要相信传过来的参数!</p> +<p>OK,那么来讲讲这次的题目,就是Golang中的参数校验库——validator。</p> +<p>用过Gin的小伙伴儿应该知道其<code>binding</code>参数验证器非常好用,其就是调用了validator。此处呢我们来介绍下validator的基础用法,和在一般场景下的应用案例。</p> +<h2 id="基础用法">基础用法</h2> +<h3 id="介绍">介绍</h3> +<p><code>validator</code>包源码在<a class="link" href="https://github.com/go-playground/validator" target="_blank" rel="noopener" + >github.com/go-playground/validator</a>。其基于标记实现结构和单个字段的值验证,包含如下关键功能:</p> +<ul> +<li>使用验证标记或自定义验证程序进行跨字段和跨结构验证</li> +<li>Slice、Array和Map都可以允许验证多维字段的任何或者所有级别</li> +<li>能够深入查看映射键和值以进行验证</li> +<li>通过在验证之前确定类型接口的基础类型来处理类型接口</li> +<li>处理自定义字段类型</li> +<li>允许将多个验证映射到单个标记,以便在结构上更轻松地定义验证</li> +<li>提取自定义定义的字段名,例如,可以指定在验证时提取JSON名称,并使其在结果FieldError中可用</li> +<li>可定制的i18n错误消息</li> +<li>gin web框架的默认验证器</li> +</ul> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go get github.com/go-playground/validator/v10 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="导入">导入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;github.com/go-playground/validator/v10&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证规则">验证规则</h3> +<p>此处从官方列举的各个类别中挑选部分举例说明。</p> +<h4 id="1比较">1)比较</h4> +<ul> +<li><strong>eq</strong>:相等</li> +<li><strong>gt</strong>:大于</li> +<li><strong>gte</strong>:大于等于</li> +<li><strong>lt</strong>:小于</li> +<li><strong>lte</strong>:小于等于</li> +<li><strong>ne</strong>:不等于</li> +</ul> +<h4 id="2字段">2)字段</h4> +<p>此处的字段大部分可以理解为上面的比较的tag跟<code>field</code>拼接而成,而中间有<code>cs</code>的tag为跨struct比较。</p> +<ul> +<li><strong>eqfield</strong>(=Field):必须等于Field的值</li> +<li><strong>nefield</strong>(=Field):必须不等于Field的值</li> +<li><strong>gtfield</strong>(=Field):必须大于Field的值</li> +<li><strong>eqcsfield</strong>(=Other.Field):必须等于struct Other中的Field的值</li> +</ul> +<h4 id="3网络">3)网络</h4> +<ul> +<li><strong>ip</strong>:网络协议地址IP</li> +<li><strong>ip4_addr</strong>:网络协议地址IPv4</li> +<li><strong>mac</strong>:mac地址</li> +<li><strong>url</strong>:url</li> +</ul> +<h4 id="4字符">4)字符</h4> +<ul> +<li><strong>ascii</strong>:ASCII</li> +<li><strong>boolean</strong>:Boolean</li> +<li><strong>endswith</strong>: 以&hellip;结尾</li> +<li><strong>contains</strong>:包含</li> +<li><strong>uppercase</strong>:大写</li> +</ul> +<h4 id="5格式">5)格式</h4> +<ul> +<li><strong>base64</strong>:Base64字符串</li> +<li><strong>base64url</strong>:Base64url字符串</li> +<li><strong>email</strong>:邮箱字符串</li> +<li><strong>json</strong>:JSON</li> +<li><strong>jwt</strong>:JSON Web Token</li> +<li><strong>latitude</strong>:纬度</li> +</ul> +<h4 id="6其它">6)其它</h4> +<ul> +<li><strong>len</strong>:长度</li> +<li><strong>max</strong>:最大值</li> +<li><strong>min</strong>:最小值</li> +<li><strong>required</strong>:字段为必须,不可空</li> +</ul> +<h4 id="7别名">7)别名</h4> +<ul> +<li><strong>iscolor</strong>:hexcolor|rgb|rgba|hsl|hsla</li> +<li><strong>country_code</strong>:iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric</li> +</ul> +<h2 id="案例">案例</h2> +<h3 id="简单验证">简单验证</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-playground/validator/v10&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,lte=10&#34;`</span> <span class="c1">// 姓名 非空,长度小于等于10 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Age</span> <span class="kt">int</span> <span class="s">`validate:&#34;required,gte=18,lte=50&#34;`</span> <span class="c1">// 年龄 非空,数字大于等于18,小于等于50 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Email</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,email&#34;`</span> <span class="c1">// 邮箱 非空,格式为email +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">FavouriteColor</span> <span class="kt">string</span> <span class="s">`validate:&#34;iscolor&#34;`</span> <span class="c1">// 喜欢的颜色 hexcolor|rgb|rgba|hsl|hsla的别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Password</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,gte=16,lte=22&#34;`</span> <span class="c1">// 密码 非空,长度大于等于16,小于等于22 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">RePassword</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,gte=16,lte=22,eqfield=Password&#34;`</span> <span class="c1">// 确认密码 非空,长度大于等于16,小于等于22,必须和字段Password相同 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Hobbies</span> <span class="p">[]</span><span class="nx">Hobby</span> <span class="s">`validate:&#34;lte=5&#34;`</span> <span class="c1">// 多个爱好 长度小于等于5 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Hobby</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> <span class="s">`validate:&#34;lte=50&#34;`</span> <span class="c1">// 爱好名称 长度小于等于50 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">validate</span> <span class="o">*</span><span class="nx">validator</span><span class="p">.</span><span class="nx">Validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 该函数验证struct +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 不会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">validateStruct</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 该函数单度验证字段 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">validateVariable</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">validateStruct</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">hobby</span> <span class="o">:=</span> <span class="nx">Hobby</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;划水&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;张三&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Age</span><span class="p">:</span> <span class="mi">48</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Email</span><span class="p">:</span> <span class="s">&#34;hi.hunterji@gmail.com&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">FavouriteColor</span><span class="p">:</span> <span class="s">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="s">&#34;1234567890123456&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">RePassword</span><span class="p">:</span> <span class="s">&#34;1234567890123456&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Hobbies</span><span class="p">:</span> <span class="p">[]</span><span class="nx">Hobby</span><span class="p">{</span><span class="nx">hobby</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Struct</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">validateVariable</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">email</span> <span class="o">:=</span> <span class="s">&#34;hi.hunterji@gmail.com&#34;</span> <span class="c1">// 此处邮箱地址格式写的是错误的,会导致报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">email</span><span class="p">,</span> <span class="s">&#34;required,email&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义验证">自定义验证</h3> +<p>自定义验证可以自己创建一个校验的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 注册校验函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">ValidateMyVal</span><span class="p">(</span><span class="nx">fl</span> <span class="nx">validator</span><span class="p">.</span><span class="nx">FieldLevel</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fl</span><span class="p">.</span><span class="nf">Field</span><span class="p">().</span><span class="nf">String</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;hello,world!&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后将其注册到validate上即可:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">validate</span><span class="p">.</span><span class="nf">RegisterValidation</span><span class="p">(</span><span class="s">&#34;is-hello&#34;</span><span class="p">,</span> <span class="nx">ValidateMyVal</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">s</span> <span class="o">:=</span> <span class="s">&#34;hello,kuari&#34;</span> <span class="c1">// 跟校验函数中的字符串不同,因此此处会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="s">&#34;is-hello&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>自定义校验可以满足开发过程中的特殊场景,通过制定规范的校验标准,可以推进团队的协作和开发效率。</p> +<h2 id="最后">最后</h2> +<p>至此便是对于<code>validator</code>的介绍了。本文篇幅较短,管中窥豹而已,基本可以满足简单场景的使用。以及本文的案例也是基于官方的案例改的,让其稍微接地气点。若有兴趣的小伙伴还是建议去完整看一下官方的文档和案例,多样的用法可以满足多样的场景,在满足代码的健壮性的同时也能确保代码的优美。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/go-playground/validator" target="_blank" rel="noopener" + >go-playground/validator</a></li> +</ul> + + + + Vite+Electron快速构建一个VUE3桌面应用(三)——打包 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + Tue, 19 Oct 2021 15:26:46 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + <h2 id="简介">简介</h2> +<p>上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a>完成了开发时的动态模块热重载功能,现在是时候来看看怎么完成最后一步——打包了。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,重点还是在于<code>mainWindow.loadURL()</code>。</p> +<p>打包后还是加载<code>http://localhost:3000</code>是无法运行的,因此,此处需要先用vite打包好,然后使用<code>electron-builder</code>加载vite打包后的文件进行打包。</p> +<p>为了代码能够根据不同环境在运行时加载<code>http://localhost:3000</code>,在打包时加载文件,此处需要使用环境变量来切换生产和开发环境。</p> +<h2 id="实现">实现</h2> +<h3 id="环境变量">环境变量</h3> +<p>此处使用环境变量<code>NODE_ENV</code>来切换生产和开发环境,生产环境为<code>NODE_ENV=production</code>,开发环境为<code>NODE_ENV=development</code>,若有其它如<code>release</code>等环境可在此基础上拓展。</p> +<h3 id="创建electron文件夹">创建electron文件夹</h3> +<p>在项目根目录下创建文件夹<code>electron</code>,将<code>main.js</code>和<code>preload.js</code>文件移动进来。其结构如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── electron +</span></span><span class="line"><span class="cl">│   ├── main.js +</span></span><span class="line"><span class="cl">│   └── preload.js +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>若还是不太明白可以看看<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >源码</a>中文件结构。</p> +<h3 id="编辑electronmainjs">编辑electron/main.js</h3> +<p>该文件主要是需要根据环境变量切换electron加载的内容,修改内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>修改后的完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// electron/main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">NODE_ENV</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s2">&#34;development&#34;</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">webContents</span><span class="p">.</span><span class="nx">openDevTools</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑packagejson">编辑package.json</h3> +<p>首先修改<code>main </code>属性,将<code>main: main.js</code>改为<code>main: electron/main.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="err">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,编辑<code>build</code>属性:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;build&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.your-website.your-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;ElectronApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 &lt;your-name&gt;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,更新<code>scripts</code>属性。</p> +<p>此处需要先安装两个库:</p> +<ul> +<li><strong><code>cross-env</code></strong>: 该库让开发者只需要注重环境变量的设置,而无需担心平台设置</li> +<li><strong><code>electron-builder</code></strong>: electron打包库</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D cross-env electron-builder +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后的<code>scripts</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> <span class="c1">// 此处需要设置环境变量以保证开发时加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> <span class="c1">// 新增打包命令 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,更新后的<code>package.json</code>完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron-builder&#34;</span><span class="p">:</span> <span class="s2">&#34;^22.13.1&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.my-website.my-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;MyApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="打包">打包</h2> +<p>直接执行打包命令即可开始打包。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:build +</span></span></code></pre></td></tr></table> +</div> +</div><p>打包完成之后,会多出两个文件夹<code>dist</code>和<code>dist_electron</code>,其文件结构如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── dist +</span></span><span class="line"><span class="cl">│   ├── assets +</span></span><span class="line"><span class="cl">│   ├── favicon.ico +</span></span><span class="line"><span class="cl">│   └── index.html +</span></span><span class="line"><span class="cl">├── dist_electron +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip.blockmap +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg.blockmap +</span></span><span class="line"><span class="cl">│   ├── builder-debug.yml +</span></span><span class="line"><span class="cl">│   ├── builder-effective-config.yaml +</span></span><span class="line"><span class="cl">│   └── mac +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>至此,便完成了打包。</p> +<p>后面再来写写关于electron的优化,减少electron打包后应用的体积。(这玩意儿确实打包下来有点大呢/狗头)</p> + + + + Vite+Electron快速构建一个VUE3桌面应用 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + Mon, 18 Oct 2021 09:58:57 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + <h2 id="简介">简介</h2> +<p>首先,介绍下<code>vite</code>和<code>Electron</code>。</p> +<ul> +<li>Vite是一种新型前端构建工具,能够显著提升前端开发体验。由尤大推出,其发动态表示“再也回不去webpack了&hellip;”</li> +<li>Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。</li> +</ul> +<p>当开始想用vue去开发一个桌面应用时,首先去搜索下,了解到当前如下两种现成方案:</p> +<ul> +<li><strong>electron-vue</strong>: 该项目集成度较好,封装较为完整,中文搜索下来文章较多也是该方案,可以直接上手去使用。但是,问题在于其内置electron的版本太低,写文章时看到的版本是2.0.4,而最新的electron版本是15.1.2。</li> +<li><strong>Vue CLI Plugin Electron Builder</strong>: 该方案是集成到到<code>vue-cli</code>中使用,使用<code>vue add electron-builder</code>后可直接上手,免去了基础配置的步骤。但是其只能在<code>vue-cli</code>下使用,无法配合<code>vite</code>来使用。</li> +</ul> +<p>因此,若要使用<code>vite</code>和<code>electron</code>,还需要自己来配置。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1</a></p> +<h2 id="创建一个vite项目">创建一个Vite项目</h2> +<h3 id="安装-vite">安装 vite</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建项目">创建项目</h3> +<p>创建命令如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite &lt;your-vue-app-name&gt; --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处创建一个项目,名为kuari。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite kuari --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="进入且运行">进入且运行</h3> +<p>进入项目,在运行前需要先安装下依赖。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> kuari +</span></span><span class="line"><span class="cl">yarn install +</span></span><span class="line"><span class="cl">yarn dev +</span></span></code></pre></td></tr></table> +</div> +</div><p>在运行命令敲下的一瞬间,几乎是已经在运行了,不愧是vite。此时按照输出,打开地址预览,即可看到初始化页面。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gveqwmm4eij61bc0u03zy02.jpg" alt="截屏2021-10-14 下午12.50.48" style="zoom:50%;" /> +<p>至此一个基础的vite项目创建完成。</p> +<h2 id="配置electron">配置Electron</h2> +<h3 id="官方文档">官方文档</h3> +<p>在<a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网的快速入门文档</a>中,有官方给出的利用html、javascript、css来创建一个electron应用的案例,vite+electron的方案也借鉴其中。</p> +<h3 id="安装">安装</h3> +<p>首先安装electron至vite应用。目前electron的版本为<code>^15.1.2,</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev electron +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置文件">配置文件</h3> +<p>####config.js</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">path</span> <span class="nx">from</span> <span class="s1">&#39;path&#39;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;./dist/&#39;</span><span class="p">),</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>main.js</code>,需要注意的是,该内容中<code>index.html</code>的加载路径跟electron官网给的配置不同。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadFile</span><span class="p">(</span><span class="s1">&#39;dist/index.html&#39;</span><span class="p">)</span> <span class="c1">// 此处跟electron官网路径不同,需要注意 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>preload.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// preload.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 所有Node.js API都可以在预加载过程中使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 它拥有与Chrome扩展一样的沙盒。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;DOMContentLoaded&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">replaceText</span> <span class="o">=</span> <span class="p">(</span><span class="nx">selector</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="nx">element</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="nx">text</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">dependency</span> <span class="k">of</span> <span class="p">[</span><span class="s1">&#39;chrome&#39;</span><span class="p">,</span> <span class="s1">&#39;node&#39;</span><span class="p">,</span> <span class="s1">&#39;electron&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">replaceText</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">dependency</span><span class="si">}</span><span class="sb">-version`</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">versions</span><span class="p">[</span><span class="nx">dependency</span><span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####json</p> +<p>为了确保能够运行相关electron的命令,需要修改<code>package.json</code>文件。</p> +<p>首先需要去设置<code>main</code>属性,electron默认会去在开始时寻找项目根目录下的<code>index.js</code>文件,此处我们使用的是<code>main.js</code>,所以需要去定义下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后我们需要新增electron的运行命令。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;electron .&#34;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>直接在终端输入如下命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:serve +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着我们就可以看到我们桌面应用就出来咯!</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gves3xrayzj612f0u0myy02.jpg" alt="截屏2021-10-14 下午1.32.38" style="zoom:50%;" /> +<h2 id="最后">最后</h2> +<p>之前做项目一直用的Vue CLI Plugin Electron Builder,这次有个项目先用electron开发一下,推一波看看,后期看情况swift重新开发一个mac的桌面应用。也刚好尝尝鲜,一直没有机会试试vite。</p> +<p>electron这个东东确实很方便,就是打包出来的应用体积太大,真的是硬伤啊。这次目标人群首先是windows用户,所以上electron吧!</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网快速入门</a></li> +<li><a class="link" href="https://vitejs.dev/" target="_blank" rel="noopener" + >Vite官网</a></li> +<li><a class="link" href="https://learnvue.co/2021/05/build-vue-3-desktop-apps-in-just-5-minutes-vite-electron-quick-start-guide/" target="_blank" rel="noopener" + >Build vue3 desktop apps in just 5 minutes</a></li> +</ul> + + + + OSS花式解锁下载文件新姿势,你学废了吗? + https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/ + Wed, 29 Sep 2021 09:22:22 +0000 + + https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/ + <h2 id="简介">简介</h2> +<p>在上一篇文章——<a class="link" href="https://mp.weixin.qq.com/s/qpSWVymlWtwlb5WUd_1M7Q" target="_blank" rel="noopener" + >前端文件花式直传OSS!后端:那我走?</a>中聊了下文件上传的几种方案,这里我们再来聊一下文件下载的花式姿势。</p> +<h2 id="精简版">精简版</h2> +<p>最常见的方式,莫过于后端存储文件在服务器上,然后通过后端接口传给前端,如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwoojpvg6j60lq068jrg02.jpg" + + + + loading="lazy" + + alt="最简版" + + +></p> +<p>该方案对于小规模、成本较低的项目非常适用,开发也较为便捷。</p> +<p>而对于有性能要求的项目,可以通过砸钱加机器、分片下载等方案提升项目性能。如果可以的话,请砸钱加机器吧!</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwq8edesqj60hq0bc75c02.jpg" alt="截屏2021-09-28 下午9.58.10" style="zoom:33%;" /> +<h2 id="中庸版">中庸版</h2> +<p>相较于上一个方案,可以砸丢丢钱整个OSS,将文件存储在OSS上,毕竟OSS上行流量不收费,如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwpuow9hnj60km0dc74n02.jpg" + + + + loading="lazy" + + alt="中庸版直传" + + +></p> +<p>那么问题来了,OSS的下行流量不是收费的吗?!</p> +<p>OK,偷偷告诉各位一个省钱小妙招/狗头,OSS内网的下行流量是不收费的!因此,可以通过后端请求OSS,获取到文件/字符串后,将其以文件流/base64数据的方式返回给前端。这样就避免了下行流量的费用。如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwp05f04jj60r3068glu02.jpg" + + + + loading="lazy" + + alt="中庸版" + + +></p> +<p>不过问题又来了,这样就还是占用了后端服务器的资源,依然会是性能的一个瓶颈。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwq8of8qcj6076075dft02.jpg" alt="截屏2021-09-28 下午10.12.06" style="zoom:50%;" /> +<h2 id="性能版">性能版</h2> +<p>基于上一个方案,可以再升级。砸丢丢钱,拉上CDN这老哥,利用CDN流量代替OSS的下行流量,既能让前端直接请求OSS资源,不占用服务器资源,也降低了成本。如图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwplrnpu2j60r20dc3z102.jpg" + + + + loading="lazy" + + alt="性能版" + + +></p> +<p>在优先性能的情况下,该方案是较优的。</p> +<p>要说缺点的话,就是CDN配置吧,需要买域名和SSL证书等,不过一次购买,后续使用体验会非常棒。CDN除了可以代替OSS的下行流量外,其优点不要太多,比如说CDN可以文件缓存、可以调度至加速节点等。</p> +<p>涉及到OSS的私有Bucket的话,只需要使用CDN的访问控制即可。其也只需要通过后端实现加密,生成文件访问URL给前端直接访问。</p> +<p>或许你会存在疑问,看上去挺麻烦的啊!但是看看CDN的价格,你肯定会有不一样的想法的。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gux8jydxbdj608v09agly02.jpg" alt="截屏2021-09-28 下午10.37.14" style="zoom:50%;" /> + + + 前端文件花式直传OSS!后端:那我走? + https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/ + Tue, 28 Sep 2021 09:53:18 +0000 + + https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/ + <h2 id="简介">简介</h2> +<p>前端还在传文件给后端吗?你们的服务器扛得住吗?什么&hellip;&hellip;老板砸钱加机器?!告辞!/狗头</p> +<p>前后端文件传输涉及数据较大,往往会成为很多项目的性能瓶颈。常见的传输方式也有不少,相对来说,OSS直传能够减轻很大压力。</p> +<p>本文我们来列举下常见的和oss直传的几种传输方式,并列举其优劣。</p> +<h2 id="常见方式">常见方式</h2> +<h3 id="表单上传">表单上传</h3> +<p>表单上传文件是最常见的方式,前后端开发小伙伴都很轻松,前端哐哐传,后端哐哐收就成了。其过程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guw3hw7k3uj60ix06q74b02.jpg" + + + + loading="lazy" + + alt="form上传" + + +></p> +<p><strong>优势:</strong></p> +<ul> +<li>简单方便,开发量小</li> +<li>前后端原生支持,无需额外第三方库支持</li> +</ul> +<h3 id="base64上传">Base64上传</h3> +<p>Base64方式上传文件,多常见于小文件,如小图片等,前后端都可直接使用<code>String</code>类型发送和接收。不过在前端,需要将文件转成base64数据,不仅会增加些性能消耗,还会增加传输数据的体积。而对于后端,如果并不是想直接存储base64数据,也还需要将其转成文件再存储,也会增加后端的性能消耗。</p> +<p>该上传方式可适用于<code>Resetful Api</code>,也可适用于文件的加密、回调接口携带文件等等。其过程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvkwpx4d9j60ix06q74d02.jpg" + + + + loading="lazy" + + alt="base64上传" + + +></p> +<p><strong>优势:</strong></p> +<ul> +<li>适用于<code>Resetful Api</code>,可用于加密、回调等场景,较为灵活</li> +</ul> +<p><strong>劣势:</strong></p> +<ul> +<li>前后端文件与base64数据转换需要消耗性能</li> +<li>只适用于小文件</li> +</ul> +<h2 id="oss直传">OSS直传</h2> +<p>此处的OSS直传方案都是使用的阿里云的OSS产品,以下将介绍三个方案,可适用于不同的场景。</p> +<h3 id="browserjs-sdk上传">Browser.js SDK上传</h3> +<p>该方案可在前端直接通过<code>browser.js</code>上传文件到OSS,可分成三步:</p> +<ol> +<li>前端使用SDK直传OSS</li> +<li>前端上传完成后请求后端,通知上传完成</li> +<li>后端检测OSS上该文件是否存在(可选)</li> +</ol> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvlvyap7zj60lc0crjru02.jpg" + + + + loading="lazy" + + alt="browser直传" + + +></p> +<p><code>Browser.js</code>的方式需要前端安装阿里云的库<code>ali-oss</code>,然后在前端调用。直传还需要OSS账户的Key和Secret,因此为了安全考虑,需要建立RAM账户,然后前端向后端先请求一个<code>STS</code>临时访问凭证来完成直传,其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvlvuqm2bj60lc0cr0tb02.jpg" + + + + loading="lazy" + + alt="browser直传2" + + +></p> +<h3 id="javascript客户端签名直传">Javascript客户端签名直传</h3> +<p>Javascript客户端签名直传,需要先从后端获取临时签名,其流程与上一步的<code>browser.js</code>方案大致相同,不同点在于:</p> +<ul> +<li>无需第三方库支持,直接表单上传</li> +<li>原生支持后端上传回调(下一步骤讲述)</li> +</ul> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvm1snim8j60lc0crgm202.jpg" + + + + loading="lazy" + + alt="javascript签名直传" + + +></p> +<p>不过该方案做下来,我感觉最大的问题是权限配置有点麻烦&hellip;&hellip;/泪眼</p> +<h3 id="服务端签名直传并设置上传回调">服务端签名直传并设置上传回调</h3> +<p>该方案其实是上面方案——Javascript客户端签名直传的升级版本,其加上了后端的上传回调功能。不错呦~</p> +<p>前端需要改动的很少,只需要在请求参数中加上<code>callback</code>参数即可,该参数为后端加密,在签名请求的响应中一起返回回来,内加密了后端回调接口。在前端直传完成后,后端回调接口将会接收到相关文件参数,包括文件路径、大小、类型等。最后OSS会将回调接口response转发给前端,响应直传OSS的请求。</p> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvmceaql3j60lc0craak02.jpg" + + + + loading="lazy" + + alt="后端签名直传且回调" + + +></p> +<h2 id="对比">对比</h2> +<p>传统方式相比直传OSS,相对来说有三个缺点:</p> +<ul> +<li>上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。</li> +<li>扩展性差:如果后续用户多了,应用服务器会成为瓶颈。</li> +<li>费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。</li> +</ul> +<p>当然,对于规模较小、成本较低的项目来说,常见的上传方式还是适合的,毕竟没有最好的,只有最适合的。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/112718.html?spm=5176.22414175.sslink.2.3fc92aca2CzF4n" target="_blank" rel="noopener" + >Web端上传介绍</a></p> +</li> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/31925.html" target="_blank" rel="noopener" + >JavaScript客户端签名直传</a></p> +</li> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/267439.html?spm=a2c4g.11186623.0.0.13415d3fbA6bxA" target="_blank" rel="noopener" + >服务端签名直传并设置上传回调</a></p> +</li> +</ul> + + + + Golang实现农历转换阳历 + https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/ + Tue, 31 Aug 2021 09:08:57 +0000 + + https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/ + <h2 id="前言">前言</h2> +<p>因为项目需求,需要去检测用户的农历生日。虽然后来找到了合适的库,但是首先先解释下<code>农历</code>的定义,也是去了解才知道,原来农历不是阴历。</p> +<p>农历属于阴阳合历,其年份分为平年和闰年。平年为十二个月,闰年为十三个月。月份分为大月和小月,大月三十天,小月二十九天,其平均历月等于一个朔望月。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Go 1.16</li> +<li>github.com/nosixtools/solarlunar 0.0.0</li> +</ul> +<h2 id="库">库</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">nosixtools</span><span class="o">/</span><span class="nx">solarlunar</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>该库支持1900~2049年。所以项目要跑到2049年后的童鞋就要注意&hellip;&hellip;</p> +<p>当然,该库还支持阳历转农历、节假日计算等,有兴趣大家可以自行去了解下。</p> +<h2 id="使用">使用</h2> +<h3 id="判断闰年">判断闰年</h3> +<p>该库不支持闰年判断,所以需要自己去实现闰年的判断,其参数类型为<code>Boolean</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">year</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">result</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">year</span><span class="o">%</span><span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">year</span><span class="o">%</span><span class="mi">100</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">year</span><span class="o">%</span><span class="mi">400</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="转换">转换</h3> +<p>需要转换的阳历日期格式是固定的,是<code>2006-01-02</code>。此处以农历<code>2021-07-17</code>为例。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lunarDate</span> <span class="o">:=</span> <span class="s">&#34;2021-07-17&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">solarlunar</span><span class="p">.</span><span class="nf">LunarToSolar</span><span class="p">(</span><span class="nx">lunarDate</span><span class="p">,</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Year</span><span class="p">())))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>输出为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="mi">2021</span><span class="o">-</span><span class="mi">08</span><span class="o">-</span><span class="mi">24</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="全部代码">全部代码</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/nosixtools/solarlunar&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lunarDate</span> <span class="o">:=</span> <span class="s">&#34;2021-07-17&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">solarlunar</span><span class="p">.</span><span class="nf">LunarToSolar</span><span class="p">(</span><span class="nx">lunarDate</span><span class="p">,</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Year</span><span class="p">())))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">year</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">result</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">year</span><span class="o">%</span><span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">year</span><span class="o">%</span><span class="mi">100</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">year</span><span class="o">%</span><span class="mi">400</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + IOS监听上下左右滑动手势 + https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/ + Mon, 30 Aug 2021 14:29:02 +0000 + + https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/ + <h2 id="前言">前言</h2> +<p>IOS监听手势使用的方法为<code>UISwipeGestureRecognizer</code>。</p> +<h2 id="添加手势监听">添加手势监听</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">gesture</span> <span class="p">=</span> <span class="n">UISwipeGestureRecognizer</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">addTarget</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="k">#selector</span><span class="p">(</span><span class="n">yourSelector</span><span class="p">(</span><span class="n">gesture</span><span class="p">:)))</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">direction</span> <span class="p">=</span> <span class="p">.</span><span class="kr">left</span> <span class="c1">// .left左滑 .right右滑 .up上滑 .down下滑</span> +</span></span><span class="line"><span class="cl"><span class="kc">self</span><span class="p">.</span><span class="n">addGestureRecognizer</span><span class="p">(</span><span class="n">gesture</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="添加响应事件">添加响应事件</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kr">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">leftPushEvent</span><span class="p">(){</span> +</span></span><span class="line"><span class="cl"> <span class="bp">print</span><span class="p">(</span><span class="s">&#34;响应...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="模板">模板</h2> +<p>把上面的整合起来,基本可以按照这个模板来写。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kr">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">leftPushEvent</span><span class="p">(){</span> +</span></span><span class="line"><span class="cl"> <span class="bp">print</span><span class="p">(</span><span class="s">&#34;响应...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">gesture</span> <span class="p">=</span> <span class="n">UISwipeGestureRecognizer</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">addTarget</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="k">#selector</span><span class="p">(</span><span class="n">leftPushEvent</span><span class="p">(</span><span class="n">gesture</span><span class="p">:)))</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">direction</span> <span class="p">=</span> <span class="p">.</span><span class="kr">left</span> +</span></span><span class="line"><span class="cl"><span class="kc">self</span><span class="p">.</span><span class="n">addGestureRecognizer</span><span class="p">(</span><span class="n">gesture</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://www.jianshu.com/p/30104b1c872d" target="_blank" rel="noopener" + >iOS手势识别&ndash;上下左右滑动</a></li> +</ul> + + + + SwiftUI+Reality开发AR项目解决全屏问题 + https://blog.hunterji.com/p/swiftui-reality%E5%BC%80%E5%8F%91ar%E9%A1%B9%E7%9B%AE%E8%A7%A3%E5%86%B3%E5%85%A8%E5%B1%8F%E9%97%AE%E9%A2%98/ + Mon, 30 Aug 2021 14:11:58 +0000 + + https://blog.hunterji.com/p/swiftui-reality%E5%BC%80%E5%8F%91ar%E9%A1%B9%E7%9B%AE%E8%A7%A3%E5%86%B3%E5%85%A8%E5%B1%8F%E9%97%AE%E9%A2%98/ + <h2 id="环境">环境</h2> +<ul> +<li>Swift 5.4</li> +<li>Xcode 12.5.1</li> +</ul> +<h2 id="问题">问题</h2> +<p>在使用<code>Swift UI</code>和<code>Realiy</code>开发AR项目时,发现摄像头一直是居中的,无法全屏。</p> +<h2 id="解决">解决</h2> +<h3 id="创建launchscreenstoryboard文件">创建<code>LaunchScreen.storyboard</code>文件</h3> +<p>在左侧文件列表中新建文件,名为<code>LaunchScreen.storyboard</code>。</p> +<h3 id="设置launch-screen-file">设置<code>Launch Screen File</code></h3> +<p>点击左侧文件列表中你的项目文件(最顶级文件),进入文件<code>[your-project].xcodeproj</code>文件。</p> +<p>在<code>General</code>中,找到<code>App Icons and Launch Images</code>,在其模块中有<code>Launch Screen File</code>选项,点击选择为<code>LaunchScreen.storyboard</code>。</p> +<p>总结下就是:<code>[yourTarget] -&gt; General -&gt; App Icons and Launch Images</code>。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/56733642/how-to-make-swiftui-view-fullscreen" target="_blank" rel="noopener" + >How to make SwiftUI view fullscreen?</a></li> +</ul> + + + + Interval计时器在tab页切换或者隐藏情况下停止运行 + https://blog.hunterji.com/p/interval%E8%AE%A1%E6%97%B6%E5%99%A8%E5%9C%A8tab%E9%A1%B5%E5%88%87%E6%8D%A2%E6%88%96%E8%80%85%E9%9A%90%E8%97%8F%E6%83%85%E5%86%B5%E4%B8%8B%E5%81%9C%E6%AD%A2%E8%BF%90%E8%A1%8C/ + Wed, 11 Aug 2021 16:20:05 +0000 + + https://blog.hunterji.com/p/interval%E8%AE%A1%E6%97%B6%E5%99%A8%E5%9C%A8tab%E9%A1%B5%E5%88%87%E6%8D%A2%E6%88%96%E8%80%85%E9%9A%90%E8%97%8F%E6%83%85%E5%86%B5%E4%B8%8B%E5%81%9C%E6%AD%A2%E8%BF%90%E8%A1%8C/ + <h2 id="问题">问题</h2> +<p>开始是开发<code>electron</code>时遇到的问题,使用<code>Interval</code>计时器,在窗口最小化隐藏再打开,计时器在隐藏期间并没有工作。</p> +<p>后来网上查询相关问题,发现更多是在浏览器tab页隐藏/切换情况下,计时器就会停止。</p> +<h2 id="解决">解决</h2> +<blockquote> +<p>在后台选项卡上运行的计时器方法可能会耗尽资源。在后台选项卡中以非常短的时间间隔运行回调的应用程序可能会消耗大量内存,以至于当前活动选项卡的工作可能会受到影响。在最坏的情况下,浏览器可能会崩溃,或者设备的电池会很快耗尽。</p> +</blockquote> +<p>此限制是浏览器限制的。</p> +<p>无法突破限制,但是可以使用折中的方式,当然我也觉得此方式相较于一直计时会更优,即监听<code>visibilitychange</code>事件。</p> +<p><code>visibilitychange</code>事件可以监听tab页面的激活与失活事件,因此可以:</p> +<ul> +<li>在失活时,记录计时器计算的最后的值,清空计时器</li> +<li>在激活时,计算失活期间应有的值,继续使用计时器计算</li> +</ul> +<h3 id="添加事件代码如下">添加事件代码如下:</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;visibilitychange&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">hidden</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// tab页失活 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// tab页激活 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li> +<p><a class="link" href="https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event" target="_blank" rel="noopener" + >https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event</a></p> +</li> +<li> +<p><a class="link" href="https://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab" target="_blank" rel="noopener" + >https://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab</a></p> +</li> +</ul> + + + + go中json解析报错invalid character '\\b' after top-level value + https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/ + Wed, 11 Aug 2021 13:38:49 +0000 + + https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/ + <h2 id="报错">报错</h2> +<p>在<code>Golang</code>中<code>json</code>解析时报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">invalid character &#39;\\b&#39; after top-level value +</span></span></code></pre></td></tr></table> +</div> +</div><p>代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">result</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">response</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="分析与排错">分析与排错</h2> +<p>首先将<code>result</code>打印出来,发现并无异常,其标点符号也没有问题。</p> +<p>然后查看网上现有解决方案的帖子基本试了下,起码对于我来说并不适用,概括下方案:</p> +<ol> +<li>遍历然后过滤,最后重组;</li> +<li>遍历,使用SetEscapeHTML(false)禁用转义符;</li> +<li>编码;</li> +<li>&hellip;</li> +</ol> +<p>最后对比代码中获取到的字符产长度和手动复制所见的字符串的长度,发现确实代码中字符长度不同,其长度是80,而手动复制的字符串的长度是72。</p> +<h2 id="解决">解决</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">strings</span><span class="p">.</span><span class="nf">ReplaceAll</span><span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="s">&#34;\b&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>就挺简单的&hellip;&hellip;</p> + + + + Golang AES-256-CBC加密和解密 + https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/ + Wed, 11 Aug 2021 13:13:12 +0000 + + https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/ + <h2 id="前言">前言</h2> +<p>项目开发中遇到该问题,网上的文章太乱,为了节省下次踩坑时间,特此记录。</p> +<h2 id="加解密">加解密</h2> +<h3 id="填充函数">填充函数</h3> +<p>该函数在加解密中都需要用到。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">PKCS5Padding</span><span class="p">(</span><span class="nx">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padding</span> <span class="o">:=</span> <span class="nx">blockSize</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">blockSize</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padText</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="nb">byte</span><span class="p">(</span><span class="nx">padding</span><span class="p">)},</span> <span class="nx">padding</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">padText</span><span class="o">...</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="加密">加密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Ase256Encrypt</span><span class="p">(</span><span class="nx">plaintext</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bKey</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bIV</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bPlaintext</span> <span class="o">:=</span> <span class="nf">PKCS5Padding</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">plaintext</span><span class="p">),</span> <span class="nx">blockSize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">(</span><span class="nx">bKey</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bPlaintext</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCEncrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="nx">bIV</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">bPlaintext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">EncodeToString</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解密">解密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">DecodeString</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;ciphertext is not a multiple of the block size&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCDecrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="全部代码">全部代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 填充 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">PKCS5Padding</span><span class="p">(</span><span class="nx">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padding</span> <span class="o">:=</span> <span class="nx">blockSize</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">blockSize</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padText</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="nb">byte</span><span class="p">(</span><span class="nx">padding</span><span class="p">)},</span> <span class="nx">padding</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">padText</span><span class="o">...</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 加密 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Ase256</span><span class="p">(</span><span class="nx">plaintext</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bKey</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bIV</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bPlaintext</span> <span class="o">:=</span> <span class="nf">PKCS5Padding</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">plaintext</span><span class="p">),</span> <span class="nx">blockSize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">(</span><span class="nx">bKey</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bPlaintext</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCEncrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="nx">bIV</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">bPlaintext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">EncodeToString</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解密 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">DecodeString</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;ciphertext is not a multiple of the block size&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCDecrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="调用">调用</h2> +<h3 id="加密-1">加密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">result</span> <span class="o">:=</span> <span class="nf">Ase256Encrypt</span><span class="p">(</span><span class="s">&#34;&lt;需要加密的数据&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;key&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;iv&gt;&#34;</span><span class="p">,</span> <span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解密-1">解密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">result</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="s">&#34;&lt;需要解密的数据&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;key&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;iv&gt;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + node spawn在windows下不生效问题记录 + https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + Thu, 29 Jul 2021 13:49:13 +0000 + + https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + <h2 id="问题描述">问题描述</h2> +<p>使用<code>electron</code>开发的windows桌面应用程序,在调用目标文件夹底下的exe执行文件时,开发机子上没有问题,但是其他机子使用时一直调用失败,也抓取不到日志。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">spawn</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">remote</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">getAppPath</span><span class="p">(),</span> <span class="s2">&#34;../target.exe&#34;</span><span class="p">),</span> <span class="p">[],</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">shell</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">detached</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">windowsHide</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="原因">原因</h2> +<p><strong>路径存在空格。</strong></p> +<p>也是经过各种原因排查,然后一次偶然的成功才注意到了路径问题,排查之后发现确实是这问题&hellip;&hellip;</p> +<h2 id="解决">解决</h2> +<p><code>spawn</code>按照如上我的代码一定条件下可以运行,其有一个参数<code>cwd</code>,用来表明运行目录。<code>spawn</code>第一个参数必须是命令的名字,不能是路径。</p> +<p>所以如上代码改成这样:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">spawn</span><span class="p">(</span><span class="s2">&#34;target.exe&#34;</span><span class="p">,</span> <span class="p">[],</span> <span class="p">{</span> <span class="c1">// 此处直接写目标exe文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">cwd</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">remote</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">getAppPath</span><span class="p">(),</span> <span class="s2">&#34;../&#34;</span><span class="p">),</span> <span class="c1">// 注意这里,使用了cwd参数来写运行目录 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">shell</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">detached</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">windowsHide</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/21356372/node-child-process-spawn-not-working-with-spaces-in-path-on-windows" target="_blank" rel="noopener" + >node child_process.spawn not working with spaces in path on windows</a></li> +</ul> + + + + SwiftUI MacOS项目alert弹出两次问题解决 + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AEalert%E5%BC%B9%E5%87%BA%E4%B8%A4%E6%AC%A1%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ + Mon, 12 Jul 2021 16:40:57 +0000 + + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AEalert%E5%BC%B9%E5%87%BA%E4%B8%A4%E6%AC%A1%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ + <h2 id="问题">问题</h2> +<p>使用<code>Alert</code>时,将其用在<code>list</code>的循环视图元素中,弹出<code>Alert</code>时,一定时长不选择就会在点击后弹出第二次。</p> +<p>这里提一下就是之前在网上看到一个帖子说他将<code>Alert</code>放在<code>NavigationView</code>上也会出现该问题。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"> <span class="n">VStask</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">,</span> <span class="n">id</span><span class="p">:</span> <span class="err">\</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span> +</span></span><span class="line"><span class="cl"> <span class="n">ElementView</span><span class="p">(</span><span class="n">item</span><span class="p">:</span> <span class="n">item</span><span class="p">)</span> <span class="c1">// 循环中的元素</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">alert</span><span class="p">(</span><span class="n">isPresented</span><span class="p">:</span> <span class="err">$</span><span class="n">showAlert</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Alert</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">title</span><span class="p">:</span> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;删除确认&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">message</span><span class="p">:</span> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;请问您确认删除该数据吗?&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">primaryButton</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;取消&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">showAlert</span> <span class="p">=</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">secondaryButton</span><span class="p">:</span> <span class="p">.</span><span class="n">destructive</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;删除&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="p">[</span><span class="n">index</span><span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>将<code>Alert</code>放到循环之前的元素上,比如<code>VStack</code>、<code>List</code>。</p> +<h2 id="参考">参考</h2> +<ul> +<li><a class="link" href="https://www.reddit.com/r/swift/comments/oahztb/swiftui_alert_showing_twice_despite_only_setting/" target="_blank" rel="noopener" + >参考帖子</a></li> +</ul> + + + + SwiftUI项目判断是否为暗黑模式 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E6%9A%97%E9%BB%91%E6%A8%A1%E5%BC%8F/ + Tue, 08 Jun 2021 09:20:38 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E6%9A%97%E9%BB%91%E6%A8%A1%E5%BC%8F/ + <h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">colorScheme</span><span class="p">)</span> <span class="kd">var</span> <span class="nv">colorScheme</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">isLight</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">colorScheme</span> <span class="p">==</span> <span class="p">.</span><span class="n">light</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="调用">调用</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Text</span><span class="p">(</span><span class="s">&#34;Hello, World !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">foregroundColor</span><span class="p">(</span><span class="n">isLight</span> <span class="p">?</span> <span class="n">Color</span><span class="p">.</span><span class="n">red</span> <span class="p">:</span> <span class="n">Color</span><span class="p">.</span><span class="n">green</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整例子">完整例子</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">SwiftUI</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">CheckIsLight</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">colorScheme</span><span class="p">)</span> <span class="kd">var</span> <span class="nv">colorScheme</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">isLight</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">colorScheme</span> <span class="p">==</span> <span class="p">.</span><span class="n">light</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Hello, World !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">foregroundColor</span><span class="p">(</span><span class="n">isLight</span> <span class="p">?</span> <span class="n">Color</span><span class="p">.</span><span class="n">red</span> <span class="p">:</span> <span class="n">Color</span><span class="p">.</span><span class="n">green</span><span class="p">)</span> <span class="c1">// 此处使用isLght实现根据暗黑模式切换字体颜色</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">CheckIsLight_Previews</span><span class="p">:</span> <span class="n">PreviewProvider</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">static</span> <span class="kd">var</span> <span class="nv">previews</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">CheckIsLight</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目调用生物识别(Touch ID -- Face ID) + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8%E7%94%9F%E7%89%A9%E8%AF%86%E5%88%ABtouch-id--face-id/ + Mon, 07 Jun 2021 17:00:00 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8%E7%94%9F%E7%89%A9%E8%AF%86%E5%88%ABtouch-id--face-id/ + <h2 id="设置权限">设置权限</h2> +<p>打开文件<code>info.plist</code>,在空白处右击,选择<code>Add Row</code>,输入选择<code>Privacy - Face ID Usage Description</code>,然后在<code>value</code>中写入<code>我们需要验证您的身份以保护数据</code>。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_face_privacy_info_plist.png" + + + + loading="lazy" + + alt="swiftui_face_privacy_info_plist.png" + + +></p> +<h2 id="代码层面接入">代码层面接入</h2> +<p>打开<code>ContentView.swift</code>文件,开始如下操作。</p> +<h3 id="引入相关库">引入相关库</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">LocalAuthentication</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建lock变量">创建lock变量</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">State</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">isUnlocked</span> <span class="p">=</span> <span class="kc">false</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>isUnlocked</code>为是否解锁,<code>true</code>表示验证完成,已解锁,<code>false</code>表示验证失败,未解锁。</p> +<h3 id="创建函数">创建函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">authenticate</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">context</span> <span class="p">=</span> <span class="n">LAContext</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">error</span><span class="p">:</span> <span class="n">NSError</span><span class="p">?</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 检查生物特征认证是否可用</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">context</span><span class="p">.</span><span class="n">canEvaluatePolicy</span><span class="p">(.</span><span class="n">deviceOwnerAuthenticationWithBiometrics</span><span class="p">,</span> <span class="n">error</span><span class="p">:</span> <span class="p">&amp;</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 可用,所以继续使用它</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">reason</span> <span class="p">=</span> <span class="s">&#34;我们需要验证您的身份以保护数据&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">context</span><span class="p">.</span><span class="n">evaluatePolicy</span><span class="p">(.</span><span class="n">deviceOwnerAuthenticationWithBiometrics</span><span class="p">,</span> <span class="n">localizedReason</span><span class="p">:</span> <span class="n">reason</span><span class="p">)</span> <span class="p">{</span> <span class="n">success</span><span class="p">,</span> <span class="n">authenticationError</span> <span class="k">in</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 身份验证现已完成</span> +</span></span><span class="line"><span class="cl"> <span class="n">DispatchQueue</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">async</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">success</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 认证成功,解锁</span> +</span></span><span class="line"><span class="cl"> <span class="kc">self</span><span class="p">.</span><span class="n">isUnlocked</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 发生的异常</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 没有生物识别</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="根据isunlocked切换view">根据isUnlocked切换View</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">VStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="kc">self</span><span class="p">.</span><span class="n">isUnlocked</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Unlocked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Locked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">onAppear</span><span class="p">(</span><span class="n">perform</span><span class="p">:</span> <span class="n">authenticate</span><span class="p">)</span> <span class="c1">// 该方法调用生物识别验证函数</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="审核问题">审核问题</h2> +<p>最近提交了ios和macos两个产品到app store,我设置的强制使用生物识别才能进入应用。但是出现的问题是macos的审核过了,而ios的审核没有过,其反馈的问题即为开始的生物识别没有过,审核人员使用的模拟器,根本不存在生物识别。</p> +<p>所以跟可能会踩这个坑的小伙伴儿提个醒,目前我还没有好的解决方案,正在等待新的审核中&hellip;&hellip;</p> + + + + SwiftUI MacOS项目根据屏幕大小调整窗口大小 + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AE%E6%A0%B9%E6%8D%AE%E5%B1%8F%E5%B9%95%E5%A4%A7%E5%B0%8F%E8%B0%83%E6%95%B4%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F/ + Mon, 07 Jun 2021 16:55:50 +0000 + + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AE%E6%A0%B9%E6%8D%AE%E5%B1%8F%E5%B9%95%E5%A4%A7%E5%B0%8F%E8%B0%83%E6%95%B4%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F/ + <h2 id="代码实现">代码实现</h2> +<h3 id="获取屏幕对象">获取屏幕对象</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">window</span> <span class="p">=</span> <span class="n">NSScreen</span><span class="p">.</span><span class="n">main</span><span class="p">?.</span><span class="n">visibleFrame</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="设置大小">设置大小</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">HStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">frame</span><span class="p">(</span><span class="n">width</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">width</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">,</span> <span class="n">height</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">height</span> <span class="o">/</span> <span class="mf">1.5</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="汇总">汇总</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">Home</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">window</span> <span class="p">=</span> <span class="n">NSScreen</span><span class="p">.</span><span class="n">main</span><span class="p">?.</span><span class="n">visibleFrame</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">HStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Hello, World!&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">frame</span><span class="p">(</span><span class="n">width</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">width</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">,</span> <span class="n">height</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">height</span> <span class="o">/</span> <span class="mf">1.5</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目实现搜索功能 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%8A%9F%E8%83%BD/ + Mon, 07 Jun 2021 16:55:39 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%8A%9F%E8%83%BD/ + <h2 id="实现">实现</h2> +<h3 id="创建变量">创建变量</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">State</span> <span class="kd">var</span> <span class="nv">search</span><span class="p">:</span> <span class="nb">String</span> <span class="p">=</span> <span class="s">&#34;&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="过滤">过滤</h3> +<p>此处过滤条件为判断元素是否包含搜索的文本。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="n">Your</span><span class="o">-</span><span class="nb">Array</span><span class="p">&gt;.</span><span class="bp">filter</span><span class="p">({</span><span class="s">&#34;</span><span class="si">\(</span><span class="nv">$0</span><span class="si">)</span><span class="s">&#34;</span><span class="p">.</span><span class="bp">contains</span><span class="p">(</span><span class="n">search</span><span class="p">.</span><span class="n">lowercased</span><span class="p">())</span> <span class="o">||</span> <span class="n">search</span><span class="p">.</span><span class="bp">isEmpty</span><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="汇总">汇总</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">DataList</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">State</span> <span class="kd">var</span> <span class="nv">search</span><span class="p">:</span> <span class="nb">String</span> <span class="p">=</span> <span class="s">&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">Binding</span> <span class="kd">var</span> <span class="nv">dataList</span><span class="p">:</span> <span class="p">[</span><span class="n">Item</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">dataSearchFilterList</span><span class="p">:</span> <span class="p">[</span><span class="n">Item</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">dataList</span><span class="p">.</span><span class="bp">filter</span><span class="p">({</span><span class="s">&#34;</span><span class="si">\(</span><span class="nv">$0</span><span class="si">)</span><span class="s">&#34;</span><span class="p">.</span><span class="bp">contains</span><span class="p">(</span><span class="n">search</span><span class="p">.</span><span class="n">lowercased</span><span class="p">())</span> <span class="o">||</span> <span class="n">search</span><span class="p">.</span><span class="bp">isEmpty</span><span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">dataSearchFilterList</span><span class="p">.</span><span class="bp">isEmpty</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;搜索不到...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 展示搜索结果</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目Image点击事件 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AEimage%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6/ + Mon, 07 Jun 2021 16:43:17 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AEimage%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6/ + <h2 id="前言">前言</h2> +<p>swiftui中的点击可以有两种情况:</p> +<ul> +<li>Button</li> +<li>Gestures</li> +</ul> +<p>根据不同情况可以去不同地使用。</p> +<h2 id="单纯的按钮">单纯的按钮</h2> +<p>此处单纯的按钮即为有按钮样式和点击事件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Button</span><span class="p">(</span><span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 点击事件触发的代码</span> +</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Image</span><span class="p">(</span><span class="n">systemName</span><span class="p">:</span> <span class="s">&#34;plus&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="无样式的按钮">无样式的按钮</h2> +<p>即为没有按钮样式的按钮,方便直接展示<code>Image</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Button</span><span class="p">(</span><span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 点击事件触发的代码</span> +</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Image</span><span class="p">(</span><span class="n">systemName</span><span class="p">:</span> <span class="s">&#34;plus&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">buttonStyle</span><span class="p">(</span><span class="n">BorderlessButtonStyle</span><span class="p">())</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="tapgesture事件">TapGesture事件</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Image</span><span class="p">(</span><span class="n">systemName</span><span class="p">:</span> <span class="s">&#34;plus&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">onTapGesture</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 点击事件触发的代码</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目复制字符串到剪切板 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%A4%8D%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + Mon, 07 Jun 2021 16:29:41 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%A4%8D%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + <h2 id="前言">前言</h2> +<p>这是个比较坑的问题,我一开始开发的是macos项目,到网上搜的方案基本都是使用<code>UIPasteboard</code>方法,但是偏偏用不了。</p> +<p>后来开发ios项目,用macos的就不行,发现<code>UIPasteboard</code>的可行,所以这里需要清楚的是,ios和macos的复制方法是不同的&hellip;&hellip;</p> +<h2 id="macos">MacOS</h2> +<h3 id="实现">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">copyToClipBoard</span><span class="p">(</span><span class="n">textToCopy</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">pasteBoard</span> <span class="p">=</span> <span class="n">NSPasteboard</span><span class="p">.</span><span class="n">general</span> +</span></span><span class="line"><span class="cl"> <span class="n">pasteBoard</span><span class="p">.</span><span class="n">clearContents</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">pasteBoard</span><span class="p">.</span><span class="n">setString</span><span class="p">(</span><span class="n">textToCopy</span><span class="p">,</span> <span class="n">forType</span><span class="p">:</span> <span class="p">.</span><span class="n">string</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="调用">调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">copyToClipBoard</span><span class="p">(</span><span class="n">textToCopy</span><span class="p">:</span> <span class="s">&#34;Hello,World!&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="ios">IOS</h2> +<h3 id="实现-1">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">UIPasteboard</span><span class="p">.</span><span class="n">general</span><span class="p">.</span><span class="n">setValue</span><span class="p">(&lt;</span><span class="n">Your</span><span class="o">-</span><span class="nb">String</span><span class="p">&gt;,</span> <span class="n">forPasteboardType</span><span class="p">:</span> <span class="n">kUTTypePlainText</span> <span class="k">as</span> <span class="nb">String</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="调用-1">调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">UIPasteboard</span><span class="p">.</span><span class="n">general</span><span class="p">.</span><span class="n">setValue</span><span class="p">(</span><span class="s">&#34;Hello,World!&#34;</span><span class="p">,</span> <span class="n">forPasteboardType</span><span class="p">:</span> <span class="n">kUTTypePlainText</span> <span class="k">as</span> <span class="nb">String</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Swift计算两个日期的天数差 + https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/ + Mon, 07 Jun 2021 15:12:29 +0000 + + https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/ + <h2 id="官方方法">官方方法</h2> +<h3 id="datecomponents"><strong>DateComponents</strong></h3> +<p>A date or time specified in terms of units (such as year, month, day, hour, and minute) to be evaluated in a calendar system and time zone.</p> +<p>以要在日历系统和时区中计算的单位(例如年、月、日、小时和分钟)指定的日期或时间。</p> +<h2 id="实现">实现</h2> +<h3 id="计算两个字符串形式的日期的天数差">计算两个字符串形式的日期的天数差</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">dateDiff</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 计算两个日期差,返回相差天数</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">calendar</span> <span class="p">=</span> <span class="n">Calendar</span><span class="p">.</span><span class="n">current</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateFormat</span> <span class="p">=</span> <span class="s">&#34;yyyy-MM-dd&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">today</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 开始日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">startDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-08&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 结束日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">endDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-09&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">diff</span><span class="p">:</span><span class="n">DateComponents</span> <span class="p">=</span> <span class="n">calendar</span><span class="p">.</span><span class="n">dateComponents</span><span class="p">([.</span><span class="n">day</span><span class="p">],</span> <span class="n">from</span><span class="p">:</span> <span class="n">startDate</span><span class="p">!,</span> <span class="n">to</span><span class="p">:</span> <span class="n">endDate</span><span class="p">!)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">diff</span><span class="p">.</span><span class="n">day</span><span class="p">!</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="计算当天跟某一天的天数差">计算当天跟某一天的天数差</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">checkDiff</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 计算两个日期差,返回相差天数</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">calendar</span> <span class="p">=</span> <span class="n">Calendar</span><span class="p">.</span><span class="n">current</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateFormat</span> <span class="p">=</span> <span class="s">&#34;yyyy-MM-dd&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当天</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">today</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">startDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="n">formatter</span><span class="p">.</span><span class="n">string</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="n">today</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 固定日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">endDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-09&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">diff</span><span class="p">:</span><span class="n">DateComponents</span> <span class="p">=</span> <span class="n">calendar</span><span class="p">.</span><span class="n">dateComponents</span><span class="p">([.</span><span class="n">day</span><span class="p">],</span> <span class="n">from</span><span class="p">:</span> <span class="n">startDate</span><span class="p">!,</span> <span class="n">to</span><span class="p">:</span> <span class="n">endDate</span><span class="p">!)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">diff</span><span class="p">.</span><span class="n">day</span><span class="p">!</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Swift UI项目调用core data + https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/ + Mon, 07 Jun 2021 11:24:22 +0000 + + https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/ + <h2 id="前言">前言</h2> +<p>这篇文章是我写的第一篇Swift UI相关的笔记吧。</p> +<p>这真的难搞哦,毕竟新出来的。国内文档真的是少之又少,国外的文档也真不多,基本搜出来的都是UIKit的。官方文档,也是一言难尽,基本就处于,我要实现一个功能,然后去搜一下各种帖子,筛选掉无用的帖子,找到有用的点,当然也是时常根本找不到有用的帖子,然后就要去油管上看各种教程,然后发现:哦!原来还有这个方法!接着去官方文档搜一下,看一下属性,自己调用调用&hellip;&hellip;</p> +<p>光是这个core data我就折腾了近一周,最后发现,原来还是官方好呀&hellip;..</p> +<p>吐槽一下自学swift ui,现在进入正题了。</p> +<p>core data呢,是苹果官方的本地数据库,但是其存储的文件其实是<code>sqlite</code>文件。其可以通过icloud实现备份和同步,当然icloud我会额外写一篇文档来详细讲述的(又是一把辛酸泪&hellip;)。</p> +<h2 id="环境">环境</h2> +<p>如下是我当前的环境:</p> +<ul> +<li>系统:macOS Big Sur 11.4</li> +<li>Xcode:Version 12.5 (12E262)</li> +<li>Swift:5.4</li> +</ul> +<h2 id="操作步骤">操作步骤</h2> +<h3 id="创建项目">创建项目</h3> +<p>在创建项目的时候,可以直接选择<code>Use Core Data</code>选项,xcode会直接在<code>ContentView.swift</code>中生成一个相关demo。</p> +<p><img src="https://blog.hunterji.com/../assets/create_swiftui_project.png" + + + + loading="lazy" + + alt="创建项目时候截图" + + +></p> +<h3 id="查看相关文件">查看相关文件</h3> +<p>创建之后,查看文件目录,相对于不选择<code>Use Core Data</code>,会多出如下几个文件:</p> +<ul> +<li><Your-Project-Name>.xcdatamodeId</li> +<li>Persistence.swift</li> +</ul> +<h3 id="创建core-data表">创建core data表</h3> +<p>点击<code>&lt;Your-Project-Name&gt;.xcdatamodeId</code>文件,进入页面。</p> +<p>可以看到页面内有默认的<code>CONFIGURATIONS</code>为<code>Default</code>,默认的<code>ENITITIES</code>为<code>Item</code>,可以理解为分别对应sql中的库和表。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_core_data_page.png" + + + + loading="lazy" + + alt="swiftui_core_data_page.png" + + +></p> +<p>然后点击<code>Item</code>,可以看到其字段,有默认的<code>timestamp</code>字段。</p> +<p>若要新增<code>ENTITIES</code>(表),点击底部的<code>Add Entity</code>按钮即可。</p> +<p>若要新增<code>Attributes</code>(字段),点击右侧<code>Attributes</code>中的<code>+</code>即可,注意字段要选择类型。</p> +<p>比如,此处以默认的<code>Item</code>为例,新增<code>username</code>和<code>age</code>两个字段。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_core_data_create_attributes.png" + + + + loading="lazy" + + alt="swiftui_core_data_create_attributes.png" + + +></p> +<h3 id="代码层面操作core-data">代码层面操作core data</h3> +<h4 id="1查看">1)查看</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">)</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">viewContext</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="n">FetchRequest</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">sortDescriptors</span><span class="p">:</span> <span class="p">[</span><span class="n">NSSortDescriptor</span><span class="p">(</span><span class="n">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="n">Item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">ascending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)],</span> +</span></span><span class="line"><span class="cl"> <span class="n">animation</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">var</span> <span class="nv">items</span><span class="p">:</span> <span class="n">FetchedResults</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// body中便利items即可</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="2新增">2)新增</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>如上是xcode自动生成的新增函数,若是要自定义添加字段可以这样改动下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// 此处按照如上添加Attributes修改,具体修改按照项目具体情况</span> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="nb">Int16</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">username</span> <span class="p">=</span> <span class="n">username</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="n">age</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3删除">3)删除</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="n">IndexSet</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">offsets</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">items</span><span class="p">[</span><span class="nv">$0</span><span class="p">]</span> <span class="p">}.</span><span class="n">forEach</span><span class="p">(</span><span class="n">viewContext</span><span class="p">.</span><span class="n">delete</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="4汇总">4)汇总</h4> +<p>在默认生成代码中,有一个<code>toolbar</code>,其在<code>macos</code>中可以生效,但是在<code>ios</code>中只有<code>EditionButton()</code>可以使用,为了方便演示,此处新增一个<code>Button</code>来添加数据。</p> +<p>其次有点要声明下,在xcode中写代码时,右侧的<code>canvas</code>会实时渲染,列表中出现的数据并不是<code>core data</code>中的数据,而是默认生成的<code>Persistence.swift</code>中生成的演示数据,只能看看,不能当真。只有在模拟器/实体机编译运行时才能操作<code>core data</code>。</p> +<p>如下为修改过后的<code>ContentView.swift</code>文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span><span class="lnt">71 +</span><span class="lnt">72 +</span><span class="lnt">73 +</span><span class="lnt">74 +</span><span class="lnt">75 +</span><span class="lnt">76 +</span><span class="lnt">77 +</span><span class="lnt">78 +</span><span class="lnt">79 +</span><span class="lnt">80 +</span><span class="lnt">81 +</span><span class="lnt">82 +</span><span class="lnt">83 +</span><span class="lnt">84 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"><span class="c1">// ContentView.swift</span> +</span></span><span class="line"><span class="cl"><span class="c1">// HelloKuari</span> +</span></span><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"><span class="c1">// Created by Kuari on 2021/6/5.</span> +</span></span><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">SwiftUI</span> +</span></span><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">CoreData</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">ContentView</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">)</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">viewContext</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">FetchRequest</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">sortDescriptors</span><span class="p">:</span> <span class="p">[</span><span class="n">NSSortDescriptor</span><span class="p">(</span><span class="n">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="n">Item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">ascending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)],</span> +</span></span><span class="line"><span class="cl"> <span class="n">animation</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">items</span><span class="p">:</span> <span class="n">FetchedResults</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">VStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">List</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Tom: </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">username</span><span class="p">!</span><span class="si">)</span><span class="s"> age: </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">age</span><span class="si">)</span><span class="s"> time : </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">!,</span> <span class="n">formatter</span><span class="p">:</span> <span class="n">itemFormatter</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">onDelete</span><span class="p">(</span><span class="n">perform</span><span class="p">:</span> <span class="n">deleteItems</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 新增一个按钮来添加数据</span> +</span></span><span class="line"><span class="cl"> <span class="n">Button</span><span class="p">(</span><span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="s">&#34;tom&#34;</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="mi">12</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Add Item&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="nb">Int16</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">username</span> <span class="p">=</span> <span class="n">username</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="n">age</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="n">IndexSet</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">offsets</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">items</span><span class="p">[</span><span class="nv">$0</span><span class="p">]</span> <span class="p">}.</span><span class="n">forEach</span><span class="p">(</span><span class="n">viewContext</span><span class="p">.</span><span class="n">delete</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">let</span> <span class="nv">itemFormatter</span><span class="p">:</span> <span class="n">DateFormatter</span> <span class="p">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateStyle</span> <span class="p">=</span> <span class="p">.</span><span class="n">short</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">timeStyle</span> <span class="p">=</span> <span class="p">.</span><span class="n">medium</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">formatter</span> +</span></span><span class="line"><span class="cl"><span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">ContentView_Previews</span><span class="p">:</span> <span class="n">PreviewProvider</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">static</span> <span class="kd">var</span> <span class="nv">previews</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ContentView</span><span class="p">().</span><span class="n">environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">,</span> <span class="n">PersistenceController</span><span class="p">.</span><span class="n">preview</span><span class="p">.</span><span class="n">container</span><span class="p">.</span><span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后点击左侧顶部的运行按钮,编译运行。</p> +<p>一开始是空白一片,点击<code>Add Item</code>按钮之后,便会开始添加数据。</p> +<img src="../assets/swiftui_core_data_add_item_demo.png" alt="swiftui_core_data_add_item_demo.png" style="zoom:50%;" /> +<p>对于记录右滑即可删除,其为<code>List</code>的<code>onDelete</code>方法。</p> +<h2 id="结语">结语</h2> +<p>该文章是面向新手的,也是记录下我踩过的坑,因为目前文档匮乏,身边也没swift的开发小伙伴儿,只能靠自己摸索,若有大佬有更好的方法,真的还请不吝赐教。</p> +<p>后面持续记录踩坑中&hellip;&hellip;</p> + + + + H5检测手机摇一摇 + https://blog.hunterji.com/p/h5%E6%A3%80%E6%B5%8B%E6%89%8B%E6%9C%BA%E6%91%87%E4%B8%80%E6%91%87/ + Wed, 28 Apr 2021 13:07:50 +0000 + + https://blog.hunterji.com/p/h5%E6%A3%80%E6%B5%8B%E6%89%8B%E6%9C%BA%E6%91%87%E4%B8%80%E6%91%87/ + <h2 id="简介">简介</h2> +<p>要实现h5检测手机摇一摇动作可以直接调用h5原生api。但是在我的实践中发现在<code>ios</code>中限制条件比较多,体验还是有些区别的。</p> +<h2 id="如何监听">如何监听</h2> +<p>调用<code>Window: devicemotion event</code>即可实现监听。<code>devicemotion</code>事件以固定的时间间隔触发,并指示设备当时在接收的加速物理力量。 它还提供有关旋转速率的信息(如果有)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleMotionEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">x</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">accelerationIncludingGravity</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">accelerationIncludingGravity</span><span class="p">.</span><span class="nx">y</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">z</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">accelerationIncludingGravity</span><span class="p">.</span><span class="nx">z</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// Do something awesome. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;devicemotion&#34;</span><span class="p">,</span> <span class="nx">handleMotionEvent</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="安卓机">安卓机</h2> +<p>安卓机上直接按照如上即可实现。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>测试摇一摇<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;phone&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;show&#34;</span><span class="p">&gt;</span>摇一摇<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleMotionEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;show&#39;</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;摇动中&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;devicemotion&#34;</span><span class="p">,</span> <span class="nx">handleMotionEvent</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s2">&#34;该浏览器不支持摇一摇功能&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="iphone">iPhone</h2> +<h3 id="限制">限制</h3> +<p>在<code>ios</code>上限制有两条:</p> +<ul> +<li>h5必须是<code>https</code>协议的</li> +<li>必须用户点击授权才可以调用<code>devicemotion</code></li> +</ul> +<h3 id="授权">授权</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getPermission</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span> <span class="o">!==</span> <span class="s1">&#39;undefined&#39;</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="s1">&#39;granted&#39;</span> <span class="o">===</span> <span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户同意授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户拒绝授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;error: &#39;</span> <span class="o">+</span> <span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>直接调用该函数请求授权会导致报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">error: NotAllowedError: Requesting device orientation or motion access requires a user gesture to prompt +</span></span></code></pre></td></tr></table> +</div> +</div><p>需要用户主动去请求授权,因此此处需要将调用放到比如一个按钮上,让用户去点击请求授权。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">&#34;getPermission()&#34;</span><span class="p">&gt;</span>请求授权<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="全部代码">全部代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>测试摇一摇<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;phone&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">&#34;getPermission()&#34;</span><span class="p">&gt;</span>请求授权<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;show&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleMotionEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;show&#39;</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;摇动中&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">startListen</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;devicemotion&#34;</span><span class="p">,</span> <span class="nx">handleMotionEvent</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s2">&#34;该浏览器不支持摇一摇功能&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getPermission</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span> <span class="o">!==</span> <span class="s1">&#39;undefined&#39;</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="s1">&#39;granted&#39;</span> <span class="o">===</span> <span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户同意授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">startListen</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户拒绝授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;error: &#39;</span> <span class="o">+</span> <span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Grafana+Loki+Docker Driver Client日志收集方案 + https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + Wed, 14 Apr 2021 15:13:18 +0000 + + https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + <h2 id="简介">简介</h2> +<p>具体日志采集方案在<code>Grafana+Loki+Promtail日志收集方案</code>文章中已经介绍过,此处不再重复介绍。不太了解的小伙伴儿赶紧去复习!</p> +<p>此处主要是记录下<code>Docker Driver Client</code>方式的部署。</p> +<h2 id="docker-plugin">docker plugin</h2> +<h3 id="安装">安装</h3> +<p>安装<code>loki</code>插件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证">验证</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ docker plugin ls +</span></span><span class="line"><span class="cl">ID NAME DESCRIPTION ENABLED +</span></span><span class="line"><span class="cl">ac720b8fcfdb loki Loki Logging Driver <span class="nb">true</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="开启禁用">开启/禁用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin <span class="nb">enable</span> loki +</span></span><span class="line"><span class="cl">docker plugin disable loki --force +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="卸载">卸载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin rm loki +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="部署">部署</h2> +<h3 id="docker-composeyml">docker-compose.yml</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/loki:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3100:3100&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/loki/local-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">grafana</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:latest</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3000:3000&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行">运行</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker-compose up -d +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="容器运行形式">容器运行形式</h3> +<p>容器运行时需要修改<code>log-driver</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> --log-driver<span class="o">=</span>loki +</span></span><span class="line"><span class="cl">--log-opt loki-url<span class="o">=</span><span class="s2">&#34;http://&lt;loki-url&gt;/loki/api/v1/push&#34;</span> +</span></span><span class="line"><span class="cl">--log-opt loki-retries<span class="o">=</span><span class="m">5</span> +</span></span><span class="line"><span class="cl">--log-opt loki-batch-size<span class="o">=</span><span class="m">400</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>举个例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run --log-driver<span class="o">=</span>loki <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-url<span class="o">=</span><span class="s2">&#34;http://192.168.10.10:3100/loki/api/v1/push&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-retries<span class="o">=</span><span class="m">5</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-batch-size<span class="o">=</span><span class="m">400</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> -p 3000:3000 <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> nginx +</span></span></code></pre></td></tr></table> +</div> +</div><p>由此查看日志,其<code>labels</code>只有<code>container_name</code>。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://grafana.com/docs/loki/latest/clients/docker-driver/" target="_blank" rel="noopener" + >官方文档</a></li> +</ul> + + + + Grafana+Loki+Promtail日志收集方案 + https://blog.hunterji.com/p/grafana-loki-promtail%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + Wed, 14 Apr 2021 14:41:47 +0000 + + https://blog.hunterji.com/p/grafana-loki-promtail%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + <h2 id="前言">前言</h2> +<h3 id="简介">简介</h3> +<p><code>Grafana</code>项目由TorkelÖdegaard于2014年发起,最近几年成为GitHub上最受欢迎的开源项目之一。 它使您可以查询,可视化指标并记录警报,无论它们存储在何处。</p> +<p><code>Loki</code>是受Prometheus启发的水平可扩展,高度可用的多租户日志聚合系统。 它被设计为非常经济高效且易于操作。 它不索引日志的内容,而是为每个日志流设置一组标签。</p> +<p><code>Promtail</code>是将本地日志内容发送到私有Loki实例或Grafana Cloud的代理。 通常将其部署到需要监视应用程序的每台机器上。</p> +<h3 id="方案对比">方案对比</h3> +<p>为什么选择<code>Grafana</code> + <code>Loki</code> + <code>Promtail</code>的日志采集方案呢?</p> +<p>我尝试过如下几种方案:</p> +<ul> +<li><code>Elasticsearch</code>+<code>Kibana</code>+<code>Filebeat</code>: 运维成本低,侵入性低,但是对于高并发情况下效果不太好,消耗资源也稍高,需要考虑日志存储成本</li> +<li><code>Elasticsearch</code>+<code>Kibana</code>+<code>Logstash</code>+<code>Kafka</code>+<code>Filebeat</code>: 可以有效处理高并发情况,且在<code>elk</code>节点挂掉情况下不会丢失日志。但是,运维成本高,需要考虑日志存储成本,整套消耗资源比较高!</li> +<li><code>Grafana</code>+<code>Loki</code>+<code>Docker Driver Client</code>:使用Docker Driver的方式来直接获取容器的日志,配置较简单,但是需要物理机上安装docker plugin,和运行容器时设置<code>log-driver</code>,侵入性较高</li> +</ul> +<p>相比之下,当前选择的方案,对于我们当前业务场景下是较为合适的,轻量且侵入性低,由于是检测日志文件,无需担心存储成本。</p> +<h2 id="通用日志收集">通用日志收集</h2> +<p>首先介绍下通用日志收集版本的部署。只需要使用默认配置即可收集普通日志,可在<code>http://&lt;your-ip&gt;:3000</code>上查看日志详情。</p> +<h3 id="docker-compose">docker-compose</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/loki:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3100:3100&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/loki/local-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">promtail</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/promtail:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">/var/log:/var/log</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/promtail/config.yml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">grafana</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:latest</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3000:3000&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加数据源">添加数据源</h3> +<p>部署成功之后,打开<code>http://&lt;your-ip&gt;:3000</code>访问<code>Grafana</code>,在左侧菜单栏选择<code>Configuration</code>,默认进去<code>Data Sources</code>页面。</p> +<p>点击<code>Add data sources</code>按钮,选择<code>Loki</code>。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_2.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.02.57" + + +></p> +<p>填入<code>URL</code>即可,此处为<code>http://loki:3100</code>,具体要看实际部署。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_3.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.03.47" + + +></p> +<p>然后点击<code>Sace &amp; Test</code>添加。</p> +<h3 id="查看与筛选日志">查看与筛选日志</h3> +<p>在左侧菜单栏选择<code>Explore</code>进入页面,点击左上角的<code>Log brwser</code>按钮,可以查看该数据源的<code>labels</code>,如此处为日志文件。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_4.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.08.48" + + +></p> +<p>在页面顶部的输入框中输入官方的<code>LogQL</code>可以筛选日志。此处日志就不展示了,大家知道有就行了。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_5.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.11.09" + + +></p> +<h2 id="docker容器日志收集">Docker容器日志收集</h2> +<p>此处详细介绍下关于docker容器日志收集。</p> +<h3 id="promtail配置文件">promtail配置文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c">##config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">server</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">http_listen_address</span><span class="p">:</span><span class="w"> </span><span class="m">0.0.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">http_listen_port</span><span class="p">:</span><span class="w"> </span><span class="m">9080</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">positions</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">filename</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/positions.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">clients</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">http://loki:3100/loki/api/v1/push</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">scrape_configs</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">job_name</span><span class="p">:</span><span class="w"> </span><span class="l">containers</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">static_configs</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">targets</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">localhost</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">job</span><span class="p">:</span><span class="w"> </span><span class="l">containerlogs</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">__path__</span><span class="p">:</span><span class="w"> </span><span class="l">/var/lib/docker/containers/*/*log</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">pipeline_stages</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">json</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">expressions</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">output</span><span class="p">:</span><span class="w"> </span><span class="l">log</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">stream</span><span class="p">:</span><span class="w"> </span><span class="l">stream</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">attrs</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">json</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">expressions</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">tag</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">attrs</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">regex</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">expression</span><span class="p">:</span><span class="w"> </span><span class="l">(?P&lt;container_name&gt;(?:[^|]*[^|]))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;tag&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">timestamp</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">format</span><span class="p">:</span><span class="w"> </span><span class="l">RFC3339Nano</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">time</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">labels</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c"># tag:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">stream</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">output</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">output</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="docker-compose-1">docker-compose</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/loki:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3100:3100&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/loki/local-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">promtail</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/promtail:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">/var/lib/docker/containers:/var/lib/docker/containers</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">./promtail-config.yaml:/mnt/config/promtail-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/mnt/config/promtail-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">grafana</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:latest</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3000:3000&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="容器日志展现形式">容器日志展现形式</h3> +<p>至此其实就已经可以在<code>Grafana</code>上看到当前容器的日志了,操作如上<code>通用日志采集</code>,但是其展现形式只是<code>filename</code>,也就是类似于<code>107728869f40afa5510879a0e372c77bb513d6154591193d375bfcd421357ed4.log</code>的以container_id展现的日志文件,难以辨认具体是哪个容器。(要是有功夫去登录服务器看下容器id,不如直接看下日志了&hellip;)</p> +<p>所以,为了能够使用容器名去查看日志,此处需要在容器启动时设置参数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">--log-driver json-file --log-opt <span class="nv">tag</span><span class="o">=</span><span class="s2">&#34;{{.Name}}&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>举个例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker run --name hello -p 8080:80 --log-driver json-file --log-opt <span class="nv">tag</span><span class="o">=</span><span class="s2">&#34;{{.Name}}&#34;</span> -d nginx +</span></span></code></pre></td></tr></table> +</div> +</div><p><img src="https://blog.hunterji.com/../assets/loki_1.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午1.55.57" + + +></p> +<p>对于使用container name,还有<a class="link" href="https://github.com/grafana/loki/issues/333" target="_blank" rel="noopener" + >另一种方案</a>,就是每次生成container_name和container_id的映射表,个人认为比较麻烦,有兴趣的小伙伴儿可以尝试下。</p> +<h3 id="参考文档">参考文档</h3> +<ul> +<li><a class="link" href="https://grafana.com/docs/loki/latest/" target="_blank" rel="noopener" + >Loki官方文档</a></li> +<li><a class="link" href="https://gist.github.com/ruanbekker/c6fa9bc6882e6f324b4319c5e3622460" target="_blank" rel="noopener" + >ruanbekker/promtail_docker_logs</a></li> +<li><a class="link" href="https://github.com/grafana/loki/issues/333" target="_blank" rel="noopener" + >Display docker logs with human/logical name</a></li> +</ul> + + + + Promise inside request interceptor + https://blog.hunterji.com/p/promise-inside-request-interceptor/ + Fri, 09 Apr 2021 11:06:25 +0000 + + https://blog.hunterji.com/p/promise-inside-request-interceptor/ + <h2 id="问题">问题</h2> +<p>在使用axios的拦截器时候,需要在request中调用一个promise函数,因此需要等待其执行完成才能去进行下一步。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getToken</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(...)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Request interceptors +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">service</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">config</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">getToken</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getToken</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(...)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Request interceptors +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">service</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="kr">async</span> <span class="nx">config</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">awit</span> <span class="nx">getToken</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + tailwindcss基础 + https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/ + Fri, 05 Feb 2021 11:16:28 +0000 + + https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/ + <h2 id="简介">简介</h2> +<blockquote> +<p>Rapidly build modern websites without ever leaving your HTML.</p> +</blockquote> +<p><a class="link" href="https://tailwindcss.com/" target="_blank" rel="noopener" + >Tailwind CSS</a>可以快速建立现代网站,而无需离开HTML。其特性是原子化,很像的<code>BootStrap</code>的css。</p> +<p>通俗点解释就是,其封装了很多独立的css样式,只需要在html中添加<code>class</code>即可调用,而不需要去从头写css样式。</p> +<h2 id="安装">安装</h2> +<h3 id="下载包">下载包</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install tailwindcss@latest postcss@latest autoprefixer@latest +</span></span></code></pre></td></tr></table> +</div> +</div><p>可能会遇到如下报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Error: PostCSS plugin tailwindcss requires PostCSS 8. +</span></span></code></pre></td></tr></table> +</div> +</div><p>那就需要降低<code>PostCSS</code>的版本。如下,先卸载,再去安装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm uninstall tailwindcss postcss autoprefixer +</span></span><span class="line"><span class="cl">npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加tailwind作为postcss插件">添加Tailwind作为PostCSS插件</h3> +<p>添加<code>tailwindcss</code>和<code>autoprefixer</code>到<code>PostCSS</code>配置。大部分情况下作为<code>postcss.config.js</code>文件放在项目的顶级路径下。其也能作为<code>.postcssrc</code>文件,或者使用<code>postcss</code>键放在<code>package.json</code>文件中。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// postcss.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">tailwindcss</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">autoprefixer</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建配置文件">创建配置文件</h3> +<p>如果想自定义安装,当使用<code>npm</code>安装<code>tailwindcss</code>时候需要使用tailwind命令行去生成一个配置文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npx tailwindcss init +</span></span></code></pre></td></tr></table> +</div> +</div><p>这将会创建一个最小化的<code>tailwind.config.js</code>文件,其位于项目的顶级路径下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// tailwind.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">purge</span><span class="o">:</span> <span class="p">[],</span> +</span></span><span class="line"><span class="cl"> <span class="nx">darkMode</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// or &#39;media&#39; or &#39;class&#39; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">theme</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">variants</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在css中包含tailwind">在CSS中包含Tailwind</h3> +<p>创建<code>styles.css</code>文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* ./your-css-folder/styles.css */</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">base</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">components</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">utilities</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>引入该文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./styles.css&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="构建css">构建CSS</h3> +<p>为生产而构建时,确保配置清除选项以删除任何最小文件大小的未使用类。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// tailwind.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">purge</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;./index.html&#34;</span><span class="p">,</span> <span class="s2">&#34;./src/**/*.{vue,js,ts,jsx,tsx}&#34;</span><span class="p">],</span> <span class="c1">// 修改此行 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">darkMode</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// or &#39;media&#39; or &#39;class&#39; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">theme</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">variants</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[]</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="简要说明">简要说明</h2> +<p>由于其样式属性巨多,此处只举几例作简要说明,讲解基础用法。在开始不熟悉的情况下,要开着其手册查询。</p> +<h3 id="width">Width</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>w-0</td> +<td>width: 0px;</td> +</tr> +<tr> +<td>w-1</td> +<td>width: 0.25rem;</td> +</tr> +<tr> +<td>w-1/2</td> +<td>width: 50%;</td> +</tr> +<tr> +<td>w-full</td> +<td>width: 100%;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;w-1/2&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="padding">Padding</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>p-0</td> +<td>padding: 0px;</td> +</tr> +<tr> +<td>p-5</td> +<td>padding: 1.25rem;</td> +</tr> +<tr> +<td>pl-1</td> +<td>padding-left: 0.25rem;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;p-5&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="position">Position</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>static</td> +<td>position: static;</td> +</tr> +<tr> +<td>fixed</td> +<td>position: fixed;</td> +</tr> +<tr> +<td>absolute</td> +<td>position: absolute;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;static&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Static parent<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;absolute bottom-0 left-0 ...&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Absolute child<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="flex垂直居中">Flex垂直居中</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-row justify-center items-center&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>1<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>2<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="简单案例">简单案例</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col justify-center items-center p-20&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in 10&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-row justify-between items-center w-1/5 bg-gray-100 m-5 p-10 cursor-pointer shadow rounded hover:shadow-lg transition duration-300 ease-in-out&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;@/assets/message.png&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;logo&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;50px&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;50px&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col ml-5&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-lg&#34;</span><span class="p">&gt;</span>今天晚上加{{ item }}个鸡腿<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-sm text-gray-500&#34;</span><span class="p">&gt;</span>2020.2.{{ item }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + sqlx建模、连接与使用 + https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/ + Fri, 25 Dec 2020 12:51:12 +0000 + + https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/ + <h2 id="前言">前言</h2> +<p>为什么要用<code>sqlx</code>而不是<code>gorm</code>呢?是因为<code>orm</code>学习成本比较高,当使用<code>python</code>时候需要使用<code>sqlalchemy</code>,遇到<code>go</code>就要换成<code>gorm</code>,换成别的语言就又有其他orm。而直接使用原生<code>sql</code>可以减少学习成本,适用于所有开发语言。其次,<code>gorm</code>本身支持软删除,但是其对软删除的支持上存在缺陷,在单条查询可以过滤软删除数据,但是在多条查询时无法有效过滤,就造成了有时候要手动过滤又有时候不要手动过滤,使用体验非常差。</p> +<p>因此此处考虑去使用<code>sqlx</code>来直接调用原生<code>sql</code>。</p> +<h2 id="安装">安装</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">go</span> <span class="nx">get</span> <span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">jmoiron</span><span class="o">/</span><span class="nx">sqlx</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="连接数据库">连接数据库</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">database</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="s">&#34;github.com/go-sql-driver/mysql&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/jmoiron/sqlx&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">DBConnect</span><span class="p">()</span> <span class="o">*</span><span class="nx">sqlx</span><span class="p">.</span><span class="nx">DB</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">env</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;NODE_ENV&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">password</span><span class="p">,</span> <span class="nx">dbname</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 使用环境变量来切换生产和开发环境 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">env</span> <span class="o">==</span> <span class="s">&#34;production&#34;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">host</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbHost&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbUser&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbPassword&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dbname</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbname&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">host</span> <span class="p">=</span> <span class="s">&#34;&lt;your-host&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="s">&#34;&lt;your-port&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="p">=</span> <span class="s">&#34;&lt;your-user&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="s">&#34;&lt;your-password&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dbname</span> <span class="p">=</span> <span class="s">&#34;&lt;your-db-name&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">dbConfig</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s:%s@tcp(%s:%s)/%s?parseTime=true&#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">password</span><span class="p">,</span> <span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">dbname</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">db</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlx</span><span class="p">.</span><span class="nf">Connect</span><span class="p">(</span><span class="s">&#34;mysql&#34;</span><span class="p">,</span> <span class="nx">dbConfig</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;failed to connect database&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">db</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="创建表和调用">创建表和调用</h2> +<h3 id="创建表">创建表</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">test_gin</span><span class="p">.</span><span class="n">todo</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">(</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">todo_id</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="n">auto_increment</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;todo标题&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;内容&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">user_id</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;用户id&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="k">timestamp</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;创建时间戳&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">updated_at</span><span class="w"> </span><span class="k">timestamp</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">update</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;更新时间戳&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">is_deleted</span><span class="w"> </span><span class="n">tinyint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;是否被删除,0:未删,1:已删&#39;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">);</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建struct">创建struct</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Todo</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TodoID</span> <span class="kt">int</span> <span class="s">`db:&#34;todo_id&#34; json:&#34;todo_id,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`db:&#34;title&#34; json:&#34;title,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`db:&#34;content&#34; json:&#34;content,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UserID</span> <span class="kt">int</span> <span class="s">`db:&#34;user_id&#34; json:&#34;user_id,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">CreatedAt</span> <span class="kt">string</span> <span class="s">`db:&#34;created_at&#34; json:&#34;created_at,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UpdatedAt</span> <span class="kt">string</span> <span class="s">`db:&#34;updated_at&#34; json:&#34;updated_at,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">IsDeleted</span> <span class="kt">bool</span> <span class="s">`db:&#34;is_deleted&#34; json:&#34;is_deleted,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="封装方法">封装方法</h3> +<p>此处以增删为例。封装常用方法是为了复用,封装时候使用害羞的代码。不需要为了封装而封装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 新增Todo +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">Todo</span><span class="p">)</span> <span class="nf">Add</span><span class="p">()</span> <span class="p">(</span><span class="nx">todoID</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 连接数据库 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 执行添加sql +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;insert into todo (title, content, user_id) value (?, ?, ?)&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">UserID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lastTodoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">result</span><span class="p">.</span><span class="nf">LastInsertId</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">todoID</span> <span class="p">=</span> <span class="nb">int</span><span class="p">(</span><span class="nx">lastTodoID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 删除Todo +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">Todo</span><span class="p">)</span> <span class="nf">Del</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;update todo set is_deleted = 0 where is_deleted = 0 and todo_id = ?&#34;</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">TodoID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="视图中使用">视图中使用</h2> +<p>此处以新增接口为例。</p> +<h3 id="调用封装的方法">调用封装的方法</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">views</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testGin/models&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// addTodo.go -- post +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AddTodo</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// {&#34;title&#34;: &#34;hello&#34;, &#34;content&#34;: &#34;world&#34;} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">requestBody</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`json:&#34;content&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBind</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">requestBody</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数有误&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">todo</span> <span class="o">:=</span> <span class="nx">models</span><span class="p">.</span><span class="nx">Todo</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span><span class="p">:</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span><span class="p">:</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UserID</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="c1">// 此处的1为假数据,此处应当从上下文获取请求用户的user_id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">todoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">todo</span><span class="p">.</span><span class="nf">Add</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;todo_id&#34;</span><span class="p">:</span> <span class="nx">todoID</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="直接使用">直接使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">views</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testGin/database&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// addTodo.go -- post +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AddTodo</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// {&#34;title&#34;: &#34;hello&#34;, &#34;content&#34;: &#34;world&#34;} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">requestBody</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`json:&#34;content&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBind</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">requestBody</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数有误&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 连接数据库 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 执行添加sql +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;insert into todo (title, content, user_id) value (?, ?, ?)&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lastTodoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">result</span><span class="p">.</span><span class="nf">LastInsertId</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;todo_id&#34;</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="nx">lastTodoID</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + gin中间件和鉴权 + https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/ + Fri, 18 Dec 2020 14:02:30 +0000 + + https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/ + <h2 id="前言">前言</h2> +<p><code>gin</code>的中间件的使用场景非常广泛,此处主要介绍如何使用其来完成常见场景下的鉴权。</p> +<h2 id="官方文档">官方文档</h2> +<p>官方文档列出了如下几种使用方式:</p> +<ul> +<li><a class="link" href="https://github.com/gin-gonic/gin#using-middleware" target="_blank" rel="noopener" + >使用中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#custom-middleware" target="_blank" rel="noopener" + >定制中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#using-basicauth-middleware" target="_blank" rel="noopener" + >使用基础认证的中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#goroutines-inside-a-middleware" target="_blank" rel="noopener" + >中间件使用协程</a></li> +</ul> +<h2 id="不同场景的鉴权实现">不同场景的鉴权实现</h2> +<h3 id="api-key">api key</h3> +<p>对于<code>api key</code>的方式需要设置白名单,对白名单外的请求进行<code>token</code>检测。此中间件在处理请求被处理之前对请求进行拦截,验证token,因此可在此处利用<code>gin.Context</code>来设置上下文,如请求所属用户的用户信息等。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">middleware</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/url&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;strings&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">whiteList</span><span class="p">()</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;/ping&#34;</span><span class="p">:</span> <span class="s">&#34;GET&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">withinWhiteList</span><span class="p">(</span><span class="nx">url</span> <span class="o">*</span><span class="nx">url</span><span class="p">.</span><span class="nx">URL</span><span class="p">,</span> <span class="nx">method</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">target</span> <span class="o">:=</span> <span class="nf">whiteList</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">queryUrl</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">url</span><span class="p">),</span> <span class="s">&#34;?&#34;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">target</span><span class="p">[</span><span class="nx">queryUrl</span><span class="p">];</span> <span class="nx">ok</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">target</span><span class="p">[</span><span class="nx">queryUrl</span><span class="p">]</span> <span class="o">==</span> <span class="nx">method</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Authorize</span><span class="p">()</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">HandlerFunc</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">type</span> <span class="nx">QueryToken</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Token</span> <span class="kt">string</span> <span class="s">`binding:&#34;required,len=3&#34; form:&#34;token&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当路由不在白名单内时进行token检测 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">!</span><span class="nf">withinWhiteList</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">URL</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">Method</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">queryToken</span> <span class="nx">QueryToken</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindQuery</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">queryToken</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">AbortWithStatusJSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">,</span> <span class="s">&#34;user&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="路由权限">路由权限</h3> +<h4 id="1说明">1)说明</h4> +<p>对于请求的处理,需要去验证是否对其请求的路径拥有访问权限。</p> +<p>首先看一下<code>gin</code>的路由设置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">group</span> <span class="o">*</span><span class="nx">RouterGroup</span><span class="p">)</span> <span class="nf">POST</span><span class="p">(</span><span class="nx">relativePath</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">handlers</span> <span class="o">...</span><span class="nx">HandlerFunc</span><span class="p">)</span> <span class="nx">IRoutes</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>其参数为<code>...HandlerFunc</code>,其解释为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">HandlerFunc</span> <span class="kd">func</span><span class="p">(</span><span class="o">*</span><span class="nx">Context</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">HandlerFunc</span> <span class="nx">defines</span> <span class="nx">the</span> <span class="nx">handler</span> <span class="nx">used</span> <span class="nx">by</span> <span class="nx">gin</span> <span class="nx">middleware</span> <span class="nx">as</span> <span class="k">return</span> <span class="nx">value</span><span class="p">.</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>所以此处可以通过定制中间件的方式实现一个路由权限处理。</p> +<p>当然此处的权限处理比较简单,使用角色直接去判断权限。如分为两个角色,管理员<code>admin</code>和普通用户<code>user</code>。</p> +<p>不过此处实现有个前提条件,就是如何拿到用户的角色呢?此处需要在上一步(<code>api key</code>)的实现中加上利用<code>gin.Context</code>设置角色:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">c</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">,</span> <span class="s">&#34;admin&#34;</span><span class="p">)</span> <span class="c1">// 可见上一步的代码,当然此处只是为了演示设置固定值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后在中间件中拿到角色并进行判断。</p> +<h4 id="2路由权限中间件">2)路由权限中间件</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">middleware</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;errors&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Permissions</span><span class="p">(</span><span class="nx">roles</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">HandlerFunc</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">permissionsErr</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 获取上下文中的用户角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">roleValue</span><span class="p">,</span> <span class="nx">exists</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">exists</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;获取用户信息失败&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">role</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">roleValue</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断请求的用户的角色是否属于设定角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">noAccess</span> <span class="o">:=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="nx">roles</span><span class="p">);</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">role</span> <span class="o">==</span> <span class="nx">roles</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">noAccess</span> <span class="p">=</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">noAccess</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;权限不够&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">permissionsErr</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">AbortWithStatusJSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3使用">3)使用</h4> +<p>在设置路由时候,添加该中间件,并设置白名单。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">r</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="nx">middleware</span><span class="p">.</span><span class="nf">Permissions</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;admin&#34;</span><span class="p">}),</span> <span class="nx">views</span><span class="p">.</span><span class="nx">AddTodo</span><span class="p">)</span> <span class="c1">// 添加中间件将会验证角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">r</span><span class="p">.</span><span class="nf">PUT</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="nx">views</span><span class="p">.</span><span class="nx">ModifyTodo</span><span class="p">)</span> <span class="c1">// 未添加中间件则不会验证角色 +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + go-Redis的发布与订阅 + https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/ + Fri, 27 Nov 2020 17:41:53 +0000 + + https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/ + <h2 id="前言">前言</h2> +<p>在数据量较小的情况下,可以使用<code>Redis</code>来实现消息的发布与订阅,来代替<code>Kafka</code>。<code>Kafka</code>对于数据量大的场景下性能卓越,但是对于如此小场景时候,不仅运维成本提升,还用不上多少性能。</p> +<p>不过使用<code>Redis</code>的另一个弊端是消息不能堆积,一旦消费者节点没有消费消息,消息将会丢失。因此需要评估当下场景来选择适合的架构。</p> +<p>此处使用go-redis来实现<code>Redis</code>的发布与订阅。</p> +<h2 id="官方文档">官方文档</h2> +<p><a class="link" href="https://pkg.go.dev/github.com/go-redis/redis/v8#PubSub" target="_blank" rel="noopener" + >官方文档</a>有较为完整的例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;mychannel1&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Wait for confirmation that subscription is created before publishing anything. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Go channel which receives messages. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Publish a message. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;mychannel1&#34;</span><span class="p">,</span> <span class="s">&#34;hello&#34;</span><span class="p">).</span><span class="nf">Err</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">time</span><span class="p">.</span><span class="nf">AfterFunc</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// When pubsub is closed channel is closed too. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Consume messages. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="代码实现">代码实现</h2> +<p>分步讲解下具体实现代码。</p> +<h3 id="连接redis">连接redis</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="发布消息">发布消息</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="订阅消息">订阅消息</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整案例">完整案例</h2> +<p>此处分为一个发布节点和一个订阅节点来实现了简单的发布与订阅。</p> +<h3 id="消息发布节点">消息发布节点</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">channel</span> <span class="o">:=</span> <span class="s">&#34;hello&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">msgList</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;hello&#34;</span><span class="p">,</span> <span class="s">&#34;world&#34;</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 此处发了两个消息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">msgList</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;已经发送%s到%s\n&#34;</span><span class="p">,</span> <span class="nx">msg</span><span class="p">,</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="消息订阅节点">消息订阅节点</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">channel</span> <span class="o">:=</span> <span class="s">&#34;hello&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行结果">运行结果</h2> +<h3 id="消息发布节点输出">消息发布节点输出</h3> +<img src="../assets/go_redis_pub.png" style="zoom:50%;" /> +<h3 id="消息订阅节点输出">消息订阅节点输出</h3> +<img src="../assets/go_redis_sub.png" style="zoom:50%;" /> + + + go单元测试 + https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + Fri, 20 Nov 2020 15:07:06 +0000 + + https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + <h2 id="前言">前言</h2> +<p>想要写出好的 Go 程序,单元测试是很重要的一部分。 <code>testing</code> 包为提供了编写单元测试所需的工具,写好单元测试后,可以通过 <code>go test</code> 命令运行测试。</p> +<h2 id="规则">规则</h2> +<p><code>testing</code> 为 Go 语言 package 提供自动化测试的支持。通过 <code>go test</code> 命令,能够自动执行如下形式的任何函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestXxx</span><span class="p">(</span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 <code>TestXxx</code> 函数,如上所述。 将该文件放在与被测试文件相同的包中。该文件将被排除在正常的程序包之外,但在运行 <code>go test</code> 命令时将被包含。</p> +<h2 id="代码结构">代码结构</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── go.mod +</span></span><span class="line"><span class="cl">├── intMinBasicDriven_test.go +</span></span><span class="line"><span class="cl">├── intMinBasic_test.go +</span></span><span class="line"><span class="cl">└── main.go +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="第一个单元测试">第一个单元测试</h2> +<h3 id="要被测试的代码">要被测试的代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// main.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IntMin</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 返回a与b中的较小值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">a</span> <span class="p">&lt;</span> <span class="nx">b</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">a</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">b</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="测试代码">测试代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// intMinBasic_test.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestIntMinBasic</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ans</span> <span class="o">:=</span> <span class="nf">IntMin</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ans</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// t.Error* 会报告测试失败的信息,然后继续运行测试。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// t.Fail* 会报告测试失败的信息,然后立即终止测试。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;IntMin(2, -2) = %d; want -2&#34;</span><span class="p">,</span> <span class="nx">ans</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行测试">运行测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go <span class="nb">test</span> +</span></span><span class="line"><span class="cl">// 输出 +</span></span><span class="line"><span class="cl">ok heihei 0.385s +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="table-driven-test">Table-Driven Test</h2> +<p>单元测试可以重复,所以会经常使用 <em>表驱动</em> 风格编写单元测试, 表中列出了输入数据,预期输出,使用循环,遍历并执行测试逻辑。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// intMinBasicDriven_test.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestIntMinTableDriven</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">tests</span> <span class="p">=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">want</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="p">}{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// t.Run 可以运行一个 “subtests” 子测试,一个子测试对应表中一行数据。 运行 go test -v 时,他们会分开显示。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tt</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tests</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">testname</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%d,%d&#34;</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">a</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">testname</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ans</span> <span class="o">:=</span> <span class="nf">IntMin</span><span class="p">(</span><span class="nx">tt</span><span class="p">.</span><span class="nx">a</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ans</span> <span class="o">!=</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;got %d, want %d&#34;</span><span class="p">,</span> <span class="nx">ans</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行代码">运行代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go <span class="nb">test</span> -v +</span></span><span class="line"><span class="cl">// <span class="nv">输出</span> +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN <span class="nv">TestIntMinTableDriven</span> +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/0,1 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/1,0 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/2,-2 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/0,-1 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/-1,0 +</span></span><span class="line"><span class="cl">--- PASS: TestIntMinTableDriven <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/0,1 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/1,0 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/2,-2 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/0,-1 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/-1,0 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl">PASS +</span></span><span class="line"><span class="cl">ok heihei 0.566s +</span></span></code></pre></td></tr></table> +</div> +</div> + + + gin的http单元测试 + https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + Fri, 20 Nov 2020 14:47:04 +0000 + + https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + <h2 id="前言">前言</h2> +<p>Go 标准库专门提供了 <code>net/http/httptest</code> 包专门用于进行 http Web 开发测试。</p> +<p>此处基于gin来实现http的单元测试。</p> +<h2 id="get请求">GET请求</h2> +<p>此处使用http单元测试对<code>/ping</code>请求测试,其正常会返回字符串<code>pong</code>,响应码为<code>200</code>。</p> +<h3 id="web应用">web应用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setupRouter</span><span class="p">()</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Engine</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/ping&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="s">&#34;pong&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="http单元测试">http单元测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http/httptest&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/stretchr/testify/assert&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestPingRoute</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建http server并发起请求 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">,</span> <span class="s">&#34;/ping&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> <span class="c1">// 断言响应码为200 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;pong&#34;</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span> <span class="c1">// 断言响应为&#34;pong&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="postputdelete请求">POST/PUT/DELETE请求</h2> +<p>post、put、delete请求处理相似,都是处理其request body,因此此处只以post为例。</p> +<p>此处对<code>/todo</code>进行测试,其正常返回为<code>json</code>,其中<code>code</code>为<code>20000</code>,响应码为<code>200</code>。</p> +<h3 id="web应用-1">web应用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setupRouter</span><span class="p">()</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Engine</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">type</span> <span class="nx">ToDo</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TodoId</span> <span class="kt">int</span> <span class="s">`binding:&#34;required&#34; json:&#34;todo_id&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">todo</span> <span class="nx">ToDo</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindJSON</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">todo</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数不全&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 处理代码,此处以打印为例,省略处理... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">todo</span><span class="p">.</span><span class="nx">TodoId</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="http单元测试-1">http单元测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestTodoCreate</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 请求方法 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">method</span> <span class="o">:=</span> <span class="s">&#34;POST&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 请求路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">urlStr</span> <span class="o">:=</span> <span class="s">&#34;/todo&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// request body +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">body</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;title&#34;</span><span class="p">:</span> <span class="s">&#34;hello&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonByte</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">urlStr</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">jsonByte</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nx">routers</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> <span class="c1">// 判断响应码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">response</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">()),</span> <span class="o">&amp;</span><span class="nx">response</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">response</span><span class="p">[</span><span class="s">&#34;code&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">20000</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="c1">// 判断自定义状态码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="封装测试请求">封装测试请求</h2> +<p>由于http单元测试代码中存在较多重复,因此此处封装重复代码。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">tests</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">TestConfig</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Url</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Method</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Body</span> <span class="kd">interface</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">tc</span> <span class="o">*</span><span class="nx">TestConfig</span><span class="p">)</span> <span class="nf">Request</span><span class="p">()</span> <span class="o">*</span><span class="nx">httptest</span><span class="p">.</span><span class="nx">ResponseRecorder</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Body</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonByte</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="p">=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Url</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">jsonByte</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="p">=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Url</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nx">routers</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">w</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="封装后的单元测试">封装后的单元测试</h2> +<p>此处以post请求为例。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAddTag</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 新增tag +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">addTestConfig</span> <span class="nx">tests</span><span class="p">.</span><span class="nx">TestConfig</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Method</span> <span class="p">=</span> <span class="s">&#34;POST&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Url</span> <span class="p">=</span> <span class="s">&#34;/tag&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Body</span> <span class="p">=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;tag_name&#34;</span><span class="p">:</span> <span class="s">&#34;HelloHello&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nf">Request</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">addResponse</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">()),</span> <span class="o">&amp;</span><span class="nx">addResponse</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">addResponse</span><span class="p">[</span><span class="s">&#34;code&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">20000</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">lastInsertTagId</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">addResponse</span><span class="p">[</span><span class="s">&#34;tag_id&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask设置全局错误捕获 + https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/ + Mon, 02 Nov 2020 20:51:02 +0000 + + https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/ + <h2 id="前言">前言</h2> +<p>代码运行过程中,意外情况会导致<code>500</code>错误,对于使用者来说体验很不好,对于开发者来说也无法及时获取错误,需要去查看日志。</p> +<p>并且有的插件在某些报错情况下会返回一些敏感信息,非常危险。因此需要去捕获全局错误,通知开发者,自定义错误消息等。</p> +<h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">app</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">werkzeug.exceptions</span> <span class="kn">import</span> <span class="n">HTTPException</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="ne">Exception</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">all_exception_handler</span><span class="p">(</span><span class="n">e</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span> <span class="o">==</span> <span class="mi">404</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">40004</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;message&#39;</span><span class="p">:</span> <span class="s1">&#39;404&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">404</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1"># 通知开发者/写入日志</span> +</span></span><span class="line"><span class="cl"> <span class="n">handle</span><span class="p">(</span><span class="n">path</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">content</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;message&#39;</span><span class="p">:</span> <span class="s1">&#39;Error&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + flask_restful限制request字段长度 + https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/ + Mon, 02 Nov 2020 20:24:35 +0000 + + https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/ + <h2 id="前言">前言</h2> +<p>当前产品遇到一个报错,就是接口收到请求没有限制请求字段长度,导致字段长度超过数据库对应字段长度,直接报了500。因此也对此有些新的需求,需要在后端限制请求字段最大长度。</p> +<h2 id="环境">环境</h2> +<ul> +<li><strong>开发语言</strong>:Python 3.7</li> +<li><strong>后端框架</strong>:Flask 1.1.1</li> +<li><strong>插件</strong>:Flask-RESTful 0.3.8</li> +</ul> +<h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">field_max_limit</span><span class="p">(</span><span class="n">max_length</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field must be String.&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_length</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field cannot exceed </span><span class="si">%i</span><span class="s2"> characters.&#34;</span> <span class="o">%</span> <span class="n">max_length</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 解析请求参数时候验证长度</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="示例">示例</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_restful</span> <span class="kn">import</span> <span class="n">Api</span><span class="p">,</span> <span class="n">Resource</span><span class="p">,</span> <span class="n">reqparse</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">werkzeug.routing</span> <span class="kn">import</span> <span class="n">ValidationError</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">api</span> <span class="o">=</span> <span class="n">Api</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">field_max_limit</span><span class="p">(</span><span class="n">max_length</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field must be String.&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_length</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field cannot exceed </span><span class="si">%i</span><span class="s2"> characters.&#34;</span> <span class="o">%</span> <span class="n">max_length</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Login</span><span class="p">(</span><span class="n">Resource</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;username&#39;</span><span class="p">:</span> <span class="n">args</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;password&#39;</span><span class="p">:</span> <span class="n">args</span><span class="o">.</span><span class="n">password</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">20000</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">api</span><span class="o">.</span><span class="n">add_resource</span><span class="p">(</span><span class="n">Login</span><span class="p">,</span> <span class="s1">&#39;/login&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Docker修改时区 + https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/ + Wed, 14 Oct 2020 23:21:39 +0000 + + https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/ + <h2 id="前言">前言</h2> +<p>在使用<code>Docker</code>时,其默认时区并非使用者所在时区,需要进行修改。对于单个容器,当前修改有几种常见方式,比如直接映射宿主机时区到容器内,而本文介绍的为使用<code>Dockerfile</code>来直接修改镜像时区。此处仅以常见几个基础容器为例来介绍。</p> +<h2 id="常见容器">常见容器</h2> +<h3 id="alpine"><strong>Alpine</strong></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> alpine:latest</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装tzdata</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apk add --no-cache tzdata<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置时区</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">TZ</span><span class="o">=</span><span class="s2">&#34;Asia/Shanghai&#34;</span><span class="err"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t alpine:time . +</span></span><span class="line"><span class="cl">docker run --rm -it alpine:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="ubuntu"><strong>Ubuntu</strong></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> ubuntu</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置localtime</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 此处需要优先设置localtime,否则安装tzdata将会进入时区选择</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装tzdata</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apt-get update <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> <span class="o">&amp;&amp;</span> apt-get install tzdata -y <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> <span class="o">&amp;&amp;</span> apt-get clean<span class="err"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t ubuntu:time . +</span></span><span class="line"><span class="cl">docker run --rm -it ubuntu:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="debian">Debian</h3> +<ul> +<li>Debian中已经安装了<code>tzdata</code>,所以跟<code>Ubuntu</code>有所不通过</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> debian</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 修改设置dpkg为自动配置</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>noninteractive +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">RUN</span> ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> dpkg-reconfigure -f noninteractive tzdata<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 修改设置dpkg为手动输入选择操作</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>dialog +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t debian:time . +</span></span><span class="line"><span class="cl">docker run --rm -it debian:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>此处不再列举太多,主要解决方式为安装<code>tzdata</code>,然后修改时区。</p> + + + + 基于python3和js的前后端aes加解密 + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + Fri, 09 Oct 2020 21:12:50 +0000 + + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + <h2 id="简述">简述</h2> +<p>在特定敏感数据的场景需要加密,一开始采用<code>rsa</code>加密,但是<code>rsa</code>加密对性能要求较高,在解密时候对于数据量限制较大,导致加密传输的数据量上限较低。而采用<code>Base64</code>虽然简单明了但是解密过于简单。因此采用折中的对称加密<code>aes</code>。</p> +<p>而<code>aes</code>加密需要前后端加密类型相同,因此此处采用<code>CTR</code>,其对加密文本没有长度限制。</p> +<h2 id="前端实现">前端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端实现">后端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="示例代码">示例代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64encode</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Util</span> <span class="kn">import</span> <span class="n">Counter</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">codecs</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">hashlib</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">convert_to_md5</span><span class="p">(</span><span class="n">info</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> md5加密 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param info: 需要加密的内容 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: md5加密密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">md5</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">info</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">md5</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesCreateKey</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成aes加密的key,key的长度必须16位 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回key的base64密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">en_key</span> <span class="o">=</span> <span class="n">convert_to_md5</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">100000</span><span class="p">,</span> <span class="mi">999999</span><span class="p">)))[</span><span class="mi">8</span><span class="p">:</span><span class="o">-</span><span class="mi">8</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">b64encode</span><span class="p">(</span><span class="n">en_key</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto" target="_blank" rel="noopener" + >https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto</a></li> +</ul> + + + + nginx报错an upstream response is buffered to a temporary file + https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/ + Sat, 15 Aug 2020 22:07:58 +0000 + + https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/ + <h2 id="报错">报错</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">an upstream response is buffered to a temporary file +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p><code>Nginx</code>配置加上如下配置</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">proxy_max_temp_file_size 0<span class="p">;</span> +</span></span><span class="line"><span class="cl">client_max_body_size 50m<span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Golang使用JSON格式存取Redis + https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/ + Tue, 11 Aug 2020 18:42:42 +0000 + + https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/ + <h2 id="前言">前言</h2> +<p>对于<code>Golang</code>操作<code>Redis</code>,此处使用<code>github.com/go-redis/redis</code>。</p> +<h2 id="操作">操作</h2> +<h3 id="连接redis服务器">连接redis服务器</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">redis</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsoniter</span> <span class="s">&#34;github.com/json-iterator/go&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BaseClient</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="o">:=</span> <span class="s">&#34;redis_server&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="o">:=</span> <span class="s">&#34;redis_port&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="o">:=</span> <span class="s">&#34;redis_password&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>###存储</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">SetJson</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">value</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{},</span> <span class="nx">expiration</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">BaseClient</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">valueString</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">jsoniter</span><span class="p">.</span><span class="nf">MarshalToString</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">valueString</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="nx">expiration</span><span class="p">)</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">).</span><span class="nf">Err</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>调用</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">SetJson</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">,</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;name&#34;</span><span class="p">:</span> <span class="s">&#34;tom&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;age&#34;</span><span class="p">:</span> <span class="mi">12</span> +</span></span><span class="line"><span class="cl"> <span class="p">}),</span> +</span></span><span class="line"><span class="cl"> <span class="mi">60</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>###读取</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">value</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">BaseClient</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">key</span><span class="p">).</span><span class="nf">Result</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>调用</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Age</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">user</span> <span class="nx">User</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">value</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>使用<code>JSON</code>格式存储与读取其实就是对目标数据在存储前和读取后进行格式转换。</p> + + + + Go生成6位随机数 + https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/ + Tue, 11 Aug 2020 09:08:20 +0000 + + https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/ + <div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;math/rand&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;strconv&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">CreateVerifyCode</span><span class="p">()</span> <span class="p">(</span><span class="nx">verifyCode</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="o">:=</span> <span class="mi">100000</span> +</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="o">:=</span> <span class="mi">999999</span> +</span></span><span class="line"><span class="cl"> <span class="nx">verifyCode</span> <span class="p">=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Itoa</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="nx">max</span><span class="o">-</span><span class="nx">min</span><span class="p">)</span> <span class="o">+</span> <span class="nx">min</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Vue按需引入ElementUI报错Error -- Plugin -- Preset files are not allowed to export objects, only functions + https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/ + Mon, 10 Aug 2020 00:00:54 +0000 + + https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/ + <h2 id="报错">报错</h2> +<p>按照ElementUI官方文档按需引入却报错,首先报错缺少<code>babel-preset-es2015</code>。安装该组件之后编译却报错。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Error: Plugin/Preset files are not allowed to <span class="nb">export</span> objects, only functions. +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>该问题为<code>babel</code>版本冲突。</p> +<h3 id="安装插件">安装插件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add @babel/preset-env +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑babelrc">编辑<code>.babelrc</code></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;presets&#34;</span><span class="o">:</span> <span class="p">[[</span><span class="s2">&#34;@babel/preset-env&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="s2">&#34;modules&#34;</span><span class="o">:</span> <span class="kc">false</span> <span class="p">}]],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;plugins&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;component&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;libraryName&#34;</span><span class="o">:</span> <span class="s2">&#34;element-ui&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;styleLibraryName&#34;</span><span class="o">:</span> <span class="s2">&#34;theme-chalk&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask_RESTful解析常见类型请求数据 + https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/ + Wed, 05 Aug 2020 15:49:56 +0000 + + https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/ + <h2 id="前言">前言</h2> +<p><code>Flask_RESTful</code>是一个Flask 扩展,它添加了快速构建 REST APIs 的支持。其请求解析接口是模仿 <code>argparse</code> 接口。它设计成提供简单并且统一的访问 Flask 中 <code>flask.request</code> 对象里的任何变量的入口。</p> +<br /> +<h2 id="常见类型解析">常见类型解析</h2> +<h3 id="基本参数">基本参数</h3> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="s2">&#34;heihei&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="必选参数">必选参数</h3> +<p>使用参数<code>required</code>。</p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="s2">&#34;heihei&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="列表string">列表[string]</h3> +<p>使用参数<code>action = 'append'</code></p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;handsome&#34;</span><span class="p">,</span> <span class="s2">&#34;cheerful&#34;</span><span class="p">,</span> <span class="s2">&#34;optimism&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">action</span> <span class="o">=</span> <span class="s1">&#39;append&#39;</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="列表dict">列表[dict]</h3> +<p>使用参数<code>action = 'append'</code></p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;friends&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;tom&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;jerry&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">action</span> <span class="o">=</span> <span class="s1">&#39;append&#39;</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="json">JSON</h3> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;character&#34;</span><span class="p">:</span> <span class="s2">&#34;optimism&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Go http请求报错x509 certificate signed by unknown authority + https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/ + Tue, 04 Aug 2020 09:58:55 +0000 + + https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/ + <h2 id="报错">报错</h2> +<p>在Go中<code>POST</code>请求时报错</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">x509: certificate signed by unknown authority +</span></span></code></pre></td></tr></table> +</div> +</div><p>即无法检验证书。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Handle</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>跳过校验即可。此处引入<code>&quot;crypto/tls&quot;</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;crypto/tls&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Handle</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">tr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Transport</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TLSClientConfig</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">tls</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="nx">InsecureSkipVerify</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">client</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Timeout</span><span class="p">:</span> <span class="mi">15</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Transport</span><span class="p">:</span> <span class="nx">tr</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SFTP部署报错解决记录 + https://blog.hunterji.com/p/sftp%E9%83%A8%E7%BD%B2%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E8%AE%B0%E5%BD%95/ + Thu, 23 Jul 2020 16:56:30 +0000 + + https://blog.hunterji.com/p/sftp%E9%83%A8%E7%BD%B2%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E8%AE%B0%E5%BD%95/ + <h2 id="前言">前言</h2> +<p>部署SFTP服务器,数次遇到几个报错,特此记录</p> +<h2 id="环境">环境</h2> +<ul> +<li> +<p>路径</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">home +</span></span><span class="line"><span class="cl">└── tom +</span></span><span class="line"><span class="cl"> └── uploads +</span></span></code></pre></td></tr></table> +</div> +</div></li> +<li> +<p>用户为<code>tom</code></p> +</li> +</ul> +<h2 id="报错">报错</h2> +<h3 id="报错一">报错一</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">permission denied +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="报错二">报错二</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bad ownership or modes <span class="k">for</span> chroot directory component <span class="s2">&#34;/home&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>以上两个报错,此处为统一解决。</p> +<ul> +<li>创建用户组</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">groupadd ftp +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>将用户加入用户组</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">usermod -a -G ftp tom +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>设置权限</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chown root:ftp -R /home/tom +</span></span><span class="line"><span class="cl">chown tom:ftp -R /home/tom/uploads +</span></span><span class="line"><span class="cl">chmod <span class="m">755</span> -R /home +</span></span></code></pre></td></tr></table> +</div> +</div> + + + flask_sqlalchemy一对一、一对多、多对一、多对多 + https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/ + Tue, 23 Jun 2020 13:46:21 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/ + <h2 id="一对多">一对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Country</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">capital</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Capital&#39;</span><span class="p">,</span> <span class="n">uselist</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Capital</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">country_id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s1">&#39;country.id&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">country</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Country&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="一对多-1">一对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">phone</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Article</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">title</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="n">index</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">body</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Text</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多对一">多对一</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Citizen</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">city_id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s1">&#39;city.id&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">city</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;City&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">City</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多对多">多对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">association_table</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Table</span><span class="p">(</span><span class="s1">&#39;association&#39;</span><span class="p">,</span><span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s1">&#39;student_id&#39;</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">Foreign</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Student</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">grade</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">teachers</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Teacher&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">secondary</span><span class="o">=</span><span class="n">association_table</span><span class="p">,</span> <span class="n">back_populates</span><span class="o">=</span><span class="s1">&#39;students&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Teacher</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">office</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + docker中php上传大小限制 + https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/ + Wed, 03 Jun 2020 15:35:33 +0000 + + https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/ + <h2 id="问题">问题</h2> +<p>使用<code>docker</code>运行的<code>wordpress</code>,有报错<code>The uploaded file exceeds the upload_max_filesize directive in php.ini</code>。</p> +<h2 id="过程">过程</h2> +<p>按照一般的方式去修改文件<code>php.ini</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">upload_max_filesize</span> <span class="o">=</span> 30M +</span></span><span class="line"><span class="cl"><span class="nv">post_max_size</span> <span class="o">=</span> 30M +</span></span></code></pre></td></tr></table> +</div> +</div><p>容器中有两个<code>php.ini</code>文件:</p> +<ul> +<li>php.ini-development</li> +<li>php.ini-production</li> +</ul> +<p>都修改之后却并不生效。</p> +<h2 id="解决">解决</h2> +<p>在文件夹<code>conf.d</code>中添加文件<code>uploads.ini</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">file_uploads = On +</span></span><span class="line"><span class="cl">memory_limit = 64M +</span></span><span class="line"><span class="cl">upload_max_filesize = 64M +</span></span><span class="line"><span class="cl">post_max_size = 64M +</span></span><span class="line"><span class="cl">max_execution_time = 600 +</span></span></code></pre></td></tr></table> +</div> +</div><p>文件夹<code>conf.d</code>位置需要看具体环境:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php -i <span class="p">|</span> grep php.ini +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后在其目录下找到文件夹<code>conf.d</code>。</p> +<p>也可以通过在运行容器时候直接将该文件映射进入。</p> + + + + Linux分享文件?快速创建静态文件服务器 + https://blog.hunterji.com/p/linux%E5%88%86%E4%BA%AB%E6%96%87%E4%BB%B6%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BA%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/ + Tue, 02 Jun 2020 19:05:56 +0000 + + https://blog.hunterji.com/p/linux%E5%88%86%E4%BA%AB%E6%96%87%E4%BB%B6%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BA%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/ + <h2 id="需求">需求</h2> +<p>Linux对于开发者来说极其友好,但是由于国内主流办公产品相关的生态较为匮乏,因此如何使用Linux去分享文件是一件十分头疼的问题。</p> +<p>对于这个问题,可以直接使用静态文件服务器解决部分需求,如下介绍几个常见方法。</p> +<h2 id="语言类">语言类</h2> +<h3 id="python">Python</h3> +<p>对于<code>Python</code>来说,可以直接使用内置的库来实现。</p> +<ul> +<li> +<p>python2</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">SimpleHTTPServer</span> <span class="mi">8000</span> +</span></span></code></pre></td></tr></table> +</div> +</div></li> +<li> +<p>Python3</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">http</span><span class="o">.</span><span class="n">server</span> <span class="mi">8000</span> +</span></span></code></pre></td></tr></table> +</div> +</div></li> +</ul> +<h3 id="nodejs">Node.js</h3> +<p><code>node</code>生态内有一个项目<code>http-server</code>,直接<code>V8</code>引擎带你飞。</p> +<ol> +<li>安装</li> +</ol> +<ul> +<li>Npm</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install --global http-server +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>Homebrew</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew install http-server +</span></span></code></pre></td></tr></table> +</div> +</div><ol start="2"> +<li>运行</li> +</ol> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">http-server <span class="o">[</span>path<span class="o">]</span> <span class="o">[</span>options<span class="o">]</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>例如:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> exmaple/ +</span></span><span class="line"><span class="cl">http-server +</span></span></code></pre></td></tr></table> +</div> +</div><ol start="3"> +<li>项目仓库地址</li> +</ol> +<p><a class="link" href="https://github.com/http-party/http-server" target="_blank" rel="noopener" + >https://github.com/http-party/http-server</a></p> +<h2 id="服务类">服务类</h2> +<ol> +<li>Nginx/Apache</li> +</ol> +<p><code>Nginx</code>和<code>Apache</code>本身可用于静态文件服务器,这就需要用户直接在本地安装。</p> +<p>当然,<code>nginx</code>需要注意配置一下,打开索引:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server { +</span></span><span class="line"><span class="cl"> listen 80; +</span></span><span class="line"><span class="cl"> ... +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> location / { +</span></span><span class="line"><span class="cl"> root /usr/share/nginx/html; +</span></span><span class="line"><span class="cl"> autoindex on; +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">} +</span></span></code></pre></td></tr></table> +</div> +</div><ol start="2"> +<li>Docker</li> +</ol> +<p>使用<code>Docker</code>其实也是使用如<code>Nginx</code>来实现静态文件服务器,但是容器化在该场景存在几大优势:</p> +<ul> +<li>即开即用</li> +<li>环境隔离</li> +</ul> +<p>相对于直接安装<code>Nginx</code>或者<code>Apache</code>,更推荐使用<code>Docker</code>。</p> + + + + vue+flask前后端信息传输非对称加密 + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + Fri, 22 May 2020 15:11:05 +0000 + + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + <h2 id="介绍">介绍</h2> +<p>为了前后端传输数据的安全性,需要对数据进行加密。因此选定使用非对称加密,此处为<code>RSA</code>。</p> +<p>在传输数据前,后端生成公钥和私钥,将公钥给前端,前端加密之后,将密文传给后端,后端使用私钥解密即可得到原始的数据。</p> +<h2 id="环境">环境</h2> +<ul> +<li><strong>前端</strong>使用<code>jsencrypt</code>加密</li> +<li><strong>后端</strong>使用<code>Crypto</code>生成密钥和解密</li> +</ul> +<h2 id="后端生成密钥">后端生成密钥</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="前端用公钥加密">前端用公钥加密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端使用私钥解密">后端使用私钥解密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>这里使用<code>base64</code>先解密一遍是必要的,否则报错<code>ValueError: Ciphertext with incorrect length.</code></li> +</ul> +<h2 id="完整代码">完整代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_encrypt</span><span class="p">(</span><span class="n">public_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa加密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params publick_key: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 需要加密的信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">public_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">encrypt</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="分段加密">分段加密</h2> +<p><code>RSA</code>加密信息最长为<code>128</code>位,过长则会报错,因此,对于过长的信息需要分段加密,后端也要分段解密后拼装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">en_str</span><span class="p">(</span><span class="nx">target_str</span><span class="p">,</span> <span class="nx">pubkey</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">en_array</span> <span class="o">=</span> <span class="p">[];</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">n</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">// 每段信息的长度 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">l</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">l</span> <span class="o">/</span> <span class="nx">n</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">message</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">n</span> <span class="o">*</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">n</span> <span class="o">*</span> <span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="nx">en_array</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">en_array</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + v-charts添加图表标题 + https://blog.hunterji.com/p/v-charts%E6%B7%BB%E5%8A%A0%E5%9B%BE%E8%A1%A8%E6%A0%87%E9%A2%98/ + Thu, 26 Mar 2020 14:54:27 +0000 + + https://blog.hunterji.com/p/v-charts%E6%B7%BB%E5%8A%A0%E5%9B%BE%E8%A1%A8%E6%A0%87%E9%A2%98/ + <h2 id="场景">场景</h2> +<p>使用 <code>v-charts</code> 做数据可视化,需要给图表添加标题。</p> +<h2 id="解决方法">解决方法</h2> +<p>v-charts本身并没有提供显示标题的配置,顾需要引入 <code>echarts</code> 的 <code>title</code> 。</p> +<h2 id="实现">实现</h2> +<h3 id="引入title">引入title</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;echarts/lib/component/title&#34;</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加标题配置">添加标题配置</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">chartTitle</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;平台用户与创客数量对比图&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">color</span><span class="o">:</span> <span class="s2">&#34;white&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ve-bar</span> <span class="na">:title</span><span class="o">=</span><span class="s">&#34;chartTitle&#34;</span> <span class="p">/&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="完整实现">完整实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">ve-bar</span> <span class="na">...</span> <span class="nt">:title</span><span class="s">=&#34;chartTitle&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;echarts/lib/component/title&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">chartTitle</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;平台用户与创客数量对比图&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">color</span><span class="o">:</span> <span class="s2">&#34;white&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="echarts配置手册">echarts配置手册</h2> +<ul> +<li><a class="link" href="https://www.echartsjs.com/zh/option.html#title" target="_blank" rel="noopener" + >https://www.echartsjs.com/zh/option.html#title</a></li> +</ul> +<h2 id="参考文章">参考文章</h2> +<ul> +<li><a class="link" href="https://github.com/ElemeFE/v-charts/issues/191" target="_blank" rel="noopener" + >https://github.com/ElemeFE/v-charts/issues/191</a></li> +</ul> + + + + vue组件props双向绑定 + https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/ + Sat, 21 Mar 2020 23:48:37 +0000 + + https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/ + <p>在vue2中不允许子组件直接修改<code>props</code>,为单项数据流,所有若要修改只能通过额外的值,并监听<code>props</code>以改变额外的值。</p> +<h2 id="设置props">设置props</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">props</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="创建额外的值">创建额外的值</h2> +<p>在<code>data</code>中创建一个<code>localDialog</code>,其值为<code>this.dialog</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">localDialog</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="监听">监听</h2> +<p>保持同步的关键在于需要在子组件内监听<code>props</code>,即此处的<code>dialog</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">watch</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="子组件向父组件传递">子组件向父组件传递</h2> +<p>子组件使用<code>this.$emit()</code>即可向父组件传递变化的值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sendToFather</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s1">&#39;dialogchange&#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="父组件调用">父组件调用</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">your-component</span> <span class="na">:dialog</span><span class="o">=</span><span class="s">&#34;dialog&#34;</span> <span class="err">@</span><span class="na">dialogchange</span><span class="o">=</span><span class="s">&#34;dialogchange&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl">data() { +</span></span><span class="line"><span class="cl"> return { +</span></span><span class="line"><span class="cl"> dialog: false +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">}, +</span></span><span class="line"><span class="cl">methods: { +</span></span><span class="line"><span class="cl"> dialogchange(val) { +</span></span><span class="line"><span class="cl"> this.dialog = val +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">} +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整代码">完整代码</h2> +<h3 id="子组件">子组件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">div</span> <span class="o">:</span><span class="nx">visible</span><span class="o">=</span><span class="s2">&#34;localDialog&#34;</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">hunterji</span><span class="p">.</span><span class="nx">com</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">button</span> <span class="err">@</span><span class="nx">click</span><span class="o">=</span><span class="s2">&#34;sendToFather&#34;</span> <span class="o">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="err">/div&gt;</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/template&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">localDialog</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">watch</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sendToFather</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s1">&#39;dialogchange&#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="父组件">父组件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">component</span> <span class="o">:</span><span class="nx">dialog</span><span class="o">=</span><span class="s2">&#34;dialog&#34;</span> <span class="err">@</span><span class="nx">dialogchange</span><span class="o">=</span><span class="s2">&#34;dialogchange&#34;</span> <span class="o">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/template&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">yourComponent</span> <span class="nx">from</span> <span class="s1">&#39;./yourComponent&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">components</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">yourComponent</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialogchange</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask_sqlalchemy的增删改查 + https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/ + Fri, 20 Mar 2020 12:19:52 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/ + <h2 id="增">增</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;article1&#39;</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s1">&#39;heihei&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>若要在新增之后获取数据库中新增数据的信息,如<code>id</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;article1&#39;</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s1">&#39;heihei&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span> <span class="c1"># 添加这一条,用于预提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 输出新增数据的信息</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="删">删</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1">##把需要删除的数据查找出来</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">content</span> <span class="o">=</span> <span class="s1">&#39;heihei&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##把这条数据删除掉</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="改">改</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1">##先把你要更改的数据查找出来</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s1">&#39;article1&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##把这条数据需要修改的地方进行修改</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article2&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="查">查</h3> +<h3 id="查询单个">查询单个</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> <span class="c1"># 或者</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article1</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article1</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="查询所有">查询所有</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">article1</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="倒叙">倒叙</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">desc</span><span class="p">())</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="限制数量">限制数量</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">limit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue实现纯前端导入与解析excel表格文件 + https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/ + Thu, 19 Mar 2020 13:11:01 +0000 + + https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/ + <h2 id="场景">场景</h2> +<p>前端导入excel表格,直接前端解析文件,将数据传给后端。</p> +<h2 id="需要的库">需要的库</h2> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install xlsx +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">XLSX</span> <span class="nx">from</span> <span class="s2">&#34;xlsx&#34;</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="代码实现">代码实现</h2> +<h3 id="html部分">html部分</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> {{ upload_file || &#34;导入&#34; }} +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">input</span> +</span></span><span class="line"><span class="cl"> <span class="na">type</span><span class="o">=</span><span class="s">&#34;file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">accept</span><span class="o">=</span><span class="s">&#34;.xls,.xlsx&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;upload_file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="err">@</span><span class="na">change</span><span class="o">=</span><span class="s">&#34;readExcel($event)&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js部分">JS部分</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">readExcel</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 读取表格文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">let</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="sr">/\.(xls|xlsx)$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()))</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$message</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;上传格式不正确,请上传xls或者xlsx格式&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;warning&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 更新获取文件名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">upload_file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">ev</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">workbook</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;binary&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">wsname</span> <span class="o">=</span> <span class="nx">workbook</span><span class="p">.</span><span class="nx">SheetNames</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//取第一张表 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">utils</span><span class="p">.</span><span class="nx">sheet_to_json</span><span class="p">(</span><span class="nx">workbook</span><span class="p">.</span><span class="nx">Sheets</span><span class="p">[</span><span class="nx">wsname</span><span class="p">]);</span> <span class="c1">//生成json表格内容 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">ws</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsBinaryString</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="整体代码">整体代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span><span class="lnt">71 +</span><span class="lnt">72 +</span><span class="lnt">73 +</span><span class="lnt">74 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">{{</span> <span class="nx">upload_file</span> <span class="o">||</span> <span class="s2">&#34;导入&#34;</span> <span class="p">}}</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">input</span> +</span></span><span class="line"><span class="cl"> <span class="na">type</span><span class="o">=</span><span class="s">&#34;file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">accept</span><span class="o">=</span><span class="s">&#34;.xls,.xlsx&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;upload_file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nt">@change</span><span class="s">=&#34;readExcel($event)&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">XLSX</span> <span class="nx">from</span> <span class="s2">&#34;xlsx&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">upload_file</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lists</span><span class="o">:</span> <span class="p">[]</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">submit_form</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 给后端发送请求,更新数据 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;假装给后端发了个请求...&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">readExcel</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 读取表格文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">let</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="sr">/\.(xls|xlsx)$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()))</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$message</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;上传格式不正确,请上传xls或者xlsx格式&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;warning&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 更新获取文件名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">upload_file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">ev</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">workbook</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;binary&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">wsname</span> <span class="o">=</span> <span class="nx">workbook</span><span class="p">.</span><span class="nx">SheetNames</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//取第一张表 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">utils</span><span class="p">.</span><span class="nx">sheet_to_json</span><span class="p">(</span><span class="nx">workbook</span><span class="p">.</span><span class="nx">Sheets</span><span class="p">[</span><span class="nx">wsname</span><span class="p">]);</span> <span class="c1">//生成json表格内容 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">lists</span> <span class="o">=</span> <span class="p">[];</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 从解析出来的数据中提取相应的数据 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ws</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">item</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">that</span><span class="p">.</span><span class="nx">lists</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">username</span><span class="o">:</span> <span class="nx">item</span><span class="p">[</span><span class="s2">&#34;用户名&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nx">phone_number</span><span class="o">:</span> <span class="nx">item</span><span class="p">[</span><span class="s2">&#34;手机号&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 给后端发请求 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">submit_form</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsBinaryString</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="样式">样式</h2> +<p>原本的文件上传样式可能会跟页面整体风格不搭,所以需要修改其样式。不过此处并不是直接修改其样式而是通过写一个<code>div</code>来覆盖原有的上传按钮。此处样式与<code>element UI</code>中的<code>primary</code>按钮样式相同。</p> +<p>实现该样式的关键在于<code>.upload_file</code>的<code>opacity</code>和<code>position</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">border</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">4</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#409eff</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">40</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">margin-top</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">15</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">min-width</span><span class="p">:</span> <span class="mi">80</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="err">*</span><span class="n">zoom</span><span class="p">:</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">upload_file</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">font-size</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">opacity</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">filter</span><span class="p">:</span> <span class="nf">alpha</span><span class="p">(</span><span class="n">opacity</span><span class="err">=</span><span class="mi">0</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">60</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="最后">最后</h2> +<p>前端的日益强大导致很多功能都可以在前端去直接实现,并且可以减少服务器压力。</p> +<p>当然单纯地去实现这样的数据传输,尤其对于重要数据,是很不安全的,因此在前后端数据传输的时候,可以加上加密校验,这个后期会来写的。</p> +<h2 id="参考文章">参考文章</h2> +<p>为了实现该功能参考了如下大佬的文章:</p> +<ul> +<li><a class="link" href="https://www.wikimoe.com/?post=157" target="_blank" rel="noopener" + >【Vue 笔记】Vue 读取excel数据并生成数组</a></li> +<li><a class="link" href="https://qxiaomay.github.io/2018/07/13/vue%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E5%B9%B6%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97/" target="_blank" rel="noopener" + >vue前端导入并解析excel表格操作指南</a></li> +<li><a class="link" href="https://www.haorooms.com/post/css_input_uploadmh" target="_blank" rel="noopener" + >css input[type=file] 样式美化,input上传按钮美化</a></li> +</ul> + + + + Flask_sqlalchemy报错MySQL server has gone away解决 + https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/ + Thu, 12 Mar 2020 21:44:36 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/ + <h3 id="报错">报错</h3> +<p>使用<code>flask_sqlalchemy</code>,服务端出现500错误,日志显示报错如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">MySQL server has gone away +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解决">解决</h3> +<p>添加配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">SQLALCHEMY_POOL_RECYCLE</span> <span class="o">=</span> <span class="mi">280</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>该配置作用是设置多少秒后回收连接,如果不提供值,默认为 2 小时。此处将其设置为280秒。</p> +<p>该配置原文解释:</p> +<blockquote> +<p>Number of seconds after which a connection is automatically recycled. This is required for MySQL, which removes connections after 8 hours idle by default. Note that Flask-SQLAlchemy automatically sets this to 2 hours if MySQL is used.</p> +</blockquote> + + + + Flask_Restful视图函数模块化 + https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/ + Sun, 23 Feb 2020 14:55:36 +0000 + + https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/ + <p>Restful作为目前流行的api设计规范,在flask上也有较好的实践,即为flask_restful。我们在使用flask_restful的时候,当代码量达到一定程度,需要将视图函数模块化。然而在Flask之前一直使用Blueprint模块化,那么flask_restful如何模块化呢?底下就来瞅瞅!</p> +<h2 id="当前架构">当前架构</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views.py +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li><code>__init__.py</code>:将该文件夹标示为一个模块</li> +<li><code>commands.py</code>:额外命令,如初始化数据库</li> +<li><code>config.py</code>:配置文件</li> +<li><code>views.py</code>:视图函数</li> +</ul> +<p>此为一个简单的flask项目架构,我们可以将flask中的各个功能拆分出来,分给不同文件,最后在<code>__init__.py</code>导入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_cors</span> <span class="kn">import</span> <span class="n">CORS</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_restful</span> <span class="kn">import</span> <span class="n">Api</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="s1">&#39;server&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="s1">&#39;config.py&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">CORS</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">api</span> <span class="o">=</span> <span class="n">Api</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span><span class="p">,</span> <span class="n">views</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="初步模块化">初步模块化</h2> +<p>现在将视图函数改为一个文件夹<code>views</code>,然后在该文件夹下放入拆分后的文件。这里以登录和获取用户信息接口为例,架构就变成了下面这样。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views +</span></span><span class="line"><span class="cl"> ├── __init__.py +</span></span><span class="line"><span class="cl"> ├── login.py +</span></span><span class="line"><span class="cl"> └── info.py +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处的<code>__init__.py</code>文件功能如上,是将文件夹<code>views</code>标示为模块,但是此处的<code>__init__py</code>是个空白文件,但是<code>server</code>文件夹中的<code>__init__.py</code>的引入需要改变。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server.views</span> <span class="kn">import</span> <span class="n">login</span><span class="p">,</span> <span class="n">info</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="最终模块化">最终模块化</h2> +<p>此时还不够,例如登录和获取用户信息接口是<code>User</code>模块下的,而获取文章标题接口是<code>Article</code>模块的下的,因此我们继续分。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views +</span></span><span class="line"><span class="cl"> ├── Aricle +</span></span><span class="line"><span class="cl"> │   ├── __init__.py +</span></span><span class="line"><span class="cl"> │   └── title.py +</span></span><span class="line"><span class="cl"> ├── User +</span></span><span class="line"><span class="cl"> │   ├── __init__.py +</span></span><span class="line"><span class="cl"> │   ├── info.py +</span></span><span class="line"><span class="cl"> │   └── login.py +</span></span><span class="line"><span class="cl"> └── __init__.py +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>server</code>文件夹中的<code>__init__.py</code>的引入继续改变。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server.views.User</span> <span class="kn">import</span> <span class="n">login</span><span class="p">,</span> <span class="n">info</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">srever.views.Article</span> <span class="kn">import</span> <span class="n">title</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>至此,关于<code>flask_restful</code>的视图函数模块化结束,若有后续改进会继续分享,若大家有更好的方式请务必分享一下。</p> +<p>当时为了解决这个问题查了很久,但是要么是介绍blueprint的,要么就是flask_restful插件入门,简直刺激&hellip;</p> + + + + vue2实现实时生成二维码和将网页合成图片并在微信内置浏览器长按保存 + https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/ + Sat, 22 Feb 2020 22:43:24 +0000 + + https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/ + <h2 id="url转为二维码">url转为二维码</h2> +<h3 id="需要的库">需要的库</h3> +<p><a class="link" href="https://www.npmjs.com/package/qrcodejs2" target="_blank" rel="noopener" + >qrcodejs2</a></p> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install qrcodejs2 --save +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="实现">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;qrcode&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">GenerateQRcode</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nx">QRCode</span><span class="p">(</span><span class="s2">&#34;qrcode&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="c1">// 此处的qrcode为上面div的id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">text</span><span class="o">:</span> <span class="nx">目标url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorDark</span><span class="o">:</span> <span class="s2">&#34;#000000&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorLight</span><span class="o">:</span> <span class="s2">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">correctLevel</span><span class="o">:</span> <span class="nx">QRCode</span><span class="p">.</span><span class="nx">CorrectLevel</span><span class="p">.</span><span class="nx">H</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">GenerateQRcode</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="网页保存为图片">网页保存为图片</h2> +<h3 id="需要的库-1">需要的库</h3> +<p><a class="link" href="https://github.com/hongru/canvas2image" target="_blank" rel="noopener" + >html2canvas</a></p> +<h3 id="安装-1">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install html2canvas --save +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="实现-1">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputImg</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetDom</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;container&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">html2canvas</span><span class="p">(</span><span class="nx">targetDom</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">canvas</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">outputImg</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="整合">整合</h2> +<p>关于小程序内置浏览器的图片下载,需要一个用来生成图片的块,还需要一个<code>img</code>,先将其隐藏。实现步骤就是首先生成二维码,然后再将html生成图片,最后在html2canvas回调中替换<code>img</code>的<code>src</code>,并将生成图片的块隐藏,将<code>img</code>显示。</p> +<p>当然关于这个实现方式,我看到的技术分享文章中,还有两种不同的解决方式:</p> +<ul> +<li>不需要html来写生成图片的块,而是使用js直接创建;</li> +<li>不需要替换隐藏,将生成的图片覆盖到html生成图片的块之前;</li> +</ul> +<p>这里我只记录一下我使用的,后期会再去研究这两种实现方式。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!--</span><span class="nx">合成的图片</span><span class="err">,</span><span class="nx">默认隐藏</span><span class="err">,</span><span class="nx">合成之后显示</span><span class="o">--&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-show</span><span class="s">=&#34;imgUrl.length&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="nt">:src</span><span class="s">=&#34;imgUrl&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;生成的图片&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;image&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!--</span><span class="nx">合成图片需要的html块</span><span class="err">,</span><span class="nx">默认显示</span><span class="err">,</span><span class="nx">合成之后隐藏</span><span class="o">--&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;container&#34;</span> <span class="nt">v-show</span><span class="s">=&#34;!imgUrl.length&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;qrcode&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span><span class="nx">长按识别二维码</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">imgUrl</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputImg</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetDom</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;container&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">html2canvas</span><span class="p">(</span><span class="nx">targetDom</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">canvas</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 将图片src替换为canvas生成之后转换的url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">imgUrl</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">GenerateQRcode</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nx">QRCode</span><span class="p">(</span><span class="s2">&#34;qrcode&#34;</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">:</span> <span class="nx">目标url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorDark</span><span class="o">:</span> <span class="s2">&#34;#000000&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorLight</span><span class="o">:</span> <span class="s2">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">correctLevel</span><span class="o">:</span> <span class="nx">QRCode</span><span class="p">.</span><span class="nx">CorrectLevel</span><span class="p">.</span><span class="nx">H</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="nx">resolve</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 先生成二维码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">GenerateQRcode</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resove</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 再合成图片 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">outputImg</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 生成之后的图片有点放肆,可以设置宽度来适应手机屏幕 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">.</span><span class="nx">image</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">100</span><span class="o">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>由此即可实现需要的功能了。</p> +<p>关于后续的优化,需要解决的图片清晰度问题、跨域图片问题等,可以参考<a class="link" href="https://segmentfault.com/a/1190000011478657" target="_blank" rel="noopener" + >这篇文章</a>,这位大佬写得很详细。</p> + + + + About + https://blog.hunterji.com/about/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://blog.hunterji.com/about/ + <p>Hello, 我是开发者小橙。</p> +<p><img src="https://blog.hunterji.com/about/weixin_qr.jpg" + width="258" + height="258" + srcset="https://blog.hunterji.com/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_480x0_resize_q75_box.jpg 480w, https://blog.hunterji.com/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_1024x0_resize_q75_box.jpg 1024w" + loading="lazy" + + alt="公众号 - 开发者小橙" + + + class="gallery-image" + data-flex-grow="100" + data-flex-basis="240px" + +></p> + + + + Search + https://blog.hunterji.com/search/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://blog.hunterji.com/search/ + + + + + diff --git "a/p/docker\344\270\255php\344\270\212\344\274\240\345\244\247\345\260\217\351\231\220\345\210\266/index.html" "b/p/docker\344\270\255php\344\270\212\344\274\240\345\244\247\345\260\217\351\231\220\345\210\266/index.html" new file mode 100644 index 0000000..8520416 --- /dev/null +++ "b/p/docker\344\270\255php\344\270\212\344\274\240\345\244\247\345\260\217\351\231\220\345\210\266/index.html" @@ -0,0 +1,668 @@ + + + + +docker中php上传大小限制 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ docker中php上传大小限制 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

问题

+

使用docker运行的wordpress,有报错The uploaded file exceeds the upload_max_filesize directive in php.ini

+

过程

+

按照一般的方式去修改文件php.ini

+
+ +
+
1
+2
+
+
upload_max_filesize = 30M
+post_max_size = 30M
+
+
+

容器中有两个php.ini文件:

+
    +
  • php.ini-development
  • +
  • php.ini-production
  • +
+

都修改之后却并不生效。

+

解决

+

在文件夹conf.d中添加文件uploads.ini

+
+ +
+
1
+2
+3
+4
+5
+
+
file_uploads = On
+memory_limit = 64M
+upload_max_filesize = 64M
+post_max_size = 64M
+max_execution_time = 600
+
+
+

文件夹conf.d位置需要看具体环境:

+
+ +
+
1
+
+
php -i | grep php.ini
+
+
+

然后在其目录下找到文件夹conf.d

+

也可以通过在运行容器时候直接将该文件映射进入。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/docker\344\277\256\346\224\271\346\227\266\345\214\272/index.html" "b/p/docker\344\277\256\346\224\271\346\227\266\345\214\272/index.html" new file mode 100644 index 0000000..cdd9d4b --- /dev/null +++ "b/p/docker\344\277\256\346\224\271\346\227\266\345\214\272/index.html" @@ -0,0 +1,731 @@ + + + + +Docker修改时区 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Docker修改时区 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

在使用Docker时,其默认时区并非使用者所在时区,需要进行修改。对于单个容器,当前修改有几种常见方式,比如直接映射宿主机时区到容器内,而本文介绍的为使用Dockerfile来直接修改镜像时区。此处仅以常见几个基础容器为例来介绍。

+

常见容器

+

Alpine

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
FROM alpine:latest
+
+# 安装tzdata
+RUN apk add --no-cache tzdata
+
+# 设置时区
+ENV TZ="Asia/Shanghai"
+
+
+
    +
  • 验证
  • +
+
+ +
+
1
+2
+
+
docker build -t alpine:time .
+docker run --rm -it alpine:time date
+
+
+

Ubuntu

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
FROM ubuntu
+
+# 设置localtime
+# 此处需要优先设置localtime,否则安装tzdata将会进入时区选择
+RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+
+# 安装tzdata
+RUN apt-get update \
+		&& apt-get install tzdata -y \
+		&& apt-get clean
+
+
+
    +
  • 验证
  • +
+
+ +
+
1
+2
+
+
docker build -t ubuntu:time .
+docker run --rm -it ubuntu:time date
+
+
+

Debian

+
    +
  • Debian中已经安装了tzdata,所以跟Ubuntu有所不通过
  • +
+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
FROM debian
+
+# 修改设置dpkg为自动配置
+ENV DEBIAN_FRONTEND=noninteractive
+
+RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+
+RUN dpkg-reconfigure -f noninteractive tzdata
+
+# 修改设置dpkg为手动输入选择操作
+ENV DEBIAN_FRONTEND=dialog
+
+
+
    +
  • 验证
  • +
+
+ +
+
1
+2
+
+
docker build -t debian:time .
+docker run --rm -it debian:time date
+
+
+

结语

+

此处不再列举太多,主要解决方式为安装tzdata,然后修改时区。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/electron\347\232\204__dirname-not-defined\346\212\245\351\224\231\350\247\243\345\206\263/index.html" "b/p/electron\347\232\204__dirname-not-defined\346\212\245\351\224\231\350\247\243\345\206\263/index.html" new file mode 100644 index 0000000..00be4ca --- /dev/null +++ "b/p/electron\347\232\204__dirname-not-defined\346\212\245\351\224\231\350\247\243\345\206\263/index.html" @@ -0,0 +1,642 @@ + + + + +electron的__dirname not defined报错解决 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ electron的__dirname not defined报错解决 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

报错

+

在使用vue cli plugin electron builder开发项目,用到涉及node相关的功能,比如fspath,出现报错__dirname not defined

+

解决

+

该问题在于需要开启electron对于node操作的支持。

+

安装@types/node

+
+ +
+
1
+
+
yarn add @types/node -D
+
+
+

修改配置

+

修改electron的配置文件,此处我的配置文件为src/background.ts

+
+ +
+
1
+2
+3
+4
+5
+
+
webPreferences: {
+	// ...
+  nodeIntegration: true,
+  nodeIntegrationInWorker: true
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/flask_restful\350\247\206\345\233\276\345\207\275\346\225\260\346\250\241\345\235\227\345\214\226/index.html" "b/p/flask_restful\350\247\206\345\233\276\345\207\275\346\225\260\346\250\241\345\235\227\345\214\226/index.html" new file mode 100644 index 0000000..4348ae8 --- /dev/null +++ "b/p/flask_restful\350\247\206\345\233\276\345\207\275\346\225\260\346\250\241\345\235\227\345\214\226/index.html" @@ -0,0 +1,752 @@ + + + + +Flask_Restful视图函数模块化 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Flask_Restful视图函数模块化 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

Restful作为目前流行的api设计规范,在flask上也有较好的实践,即为flask_restful。我们在使用flask_restful的时候,当代码量达到一定程度,需要将视图函数模块化。然而在Flask之前一直使用Blueprint模块化,那么flask_restful如何模块化呢?底下就来瞅瞅!

+

当前架构

+
+ +
+
1
+2
+3
+4
+5
+
+
server/
+├── __init__.py
+├── commands.py
+├── config.py
+└── views.py
+
+
+
    +
  • __init__.py:将该文件夹标示为一个模块
  • +
  • commands.py:额外命令,如初始化数据库
  • +
  • config.py:配置文件
  • +
  • views.py:视图函数
  • +
+

此为一个简单的flask项目架构,我们可以将flask中的各个功能拆分出来,分给不同文件,最后在__init__.py导入。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
from flask import Flask
+from flask_cors import CORS
+from flask_restful import Api
+
+app = Flask('server')
+app.config.from_pyfile('config.py')
+
+CORS(app)
+
+api = Api(app)
+
+from server import commands, views
+
+
+

初步模块化

+

现在将视图函数改为一个文件夹views,然后在该文件夹下放入拆分后的文件。这里以登录和获取用户信息接口为例,架构就变成了下面这样。

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
server/
+├── __init__.py
+├── commands.py
+├── config.py
+└── views
+    ├── __init__.py
+    ├── login.py
+    └── info.py
+
+
+

此处的__init__.py文件功能如上,是将文件夹views标示为模块,但是此处的__init__py是个空白文件,但是server文件夹中的__init__.py的引入需要改变。

+
+ +
+
1
+2
+3
+
+
...
+from server import commands
+from server.views import login, info
+
+
+

最终模块化

+

此时还不够,例如登录和获取用户信息接口是User模块下的,而获取文章标题接口是Article模块的下的,因此我们继续分。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
server/
+├── __init__.py
+├── commands.py
+├── config.py
+└── views
+    ├── Aricle
+    │   ├── __init__.py
+    │   └── title.py
+    ├── User
+    │   ├── __init__.py
+    │   ├── info.py
+    │   └── login.py
+    └── __init__.py
+
+
+

server文件夹中的__init__.py的引入继续改变。

+
+ +
+
1
+2
+3
+4
+
+
...
+from server import commands
+from server.views.User import login, info
+from srever.views.Article import title
+
+
+

结语

+

至此,关于flask_restful的视图函数模块化结束,若有后续改进会继续分享,若大家有更好的方式请务必分享一下。

+

当时为了解决这个问题查了很久,但是要么是介绍blueprint的,要么就是flask_restful插件入门,简直刺激…

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/flask_restful\350\247\243\346\236\220\345\270\270\350\247\201\347\261\273\345\236\213\350\257\267\346\261\202\346\225\260\346\215\256/index.html" "b/p/flask_restful\350\247\243\346\236\220\345\270\270\350\247\201\347\261\273\345\236\213\350\257\267\346\261\202\346\225\260\346\215\256/index.html" new file mode 100644 index 0000000..74fd8b3 --- /dev/null +++ "b/p/flask_restful\350\247\243\346\236\220\345\270\270\350\247\201\347\261\273\345\236\213\350\257\267\346\261\202\346\225\260\346\215\256/index.html" @@ -0,0 +1,825 @@ + + + + +Flask_RESTful解析常见类型请求数据 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Flask_RESTful解析常见类型请求数据 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

Flask_RESTful是一个Flask 扩展,它添加了快速构建 REST APIs 的支持。其请求解析接口是模仿 argparse 接口。它设计成提供简单并且统一的访问 Flask 中 flask.request 对象里的任何变量的入口。

+
+

常见类型解析

+

基本参数

+
    +
  • 请求
  • +
+
+ +
+
1
+2
+3
+4
+
+
{
+  "username": "kuari",
+  "info": "heihei"
+}
+
+
+
    +
  • 解析
  • +
+
+ +
+
1
+2
+3
+4
+
+
parse = reqparse.RequestParser()
+parse.add_argument('username', type = str)
+parse.add_argument('info', type = str)
+args = parse.parse_args()
+
+
+

+

必选参数

+

使用参数required

+
    +
  • 请求
  • +
+
+ +
+
1
+2
+3
+4
+
+
{
+  "username": "kuari",
+  "info": "heihei"
+}
+
+
+
    +
  • 解析
  • +
+
+ +
+
1
+2
+3
+4
+
+
parse = reqparse.RequestParser()
+parse.add_argument('username', type = str, required = True)
+parse.add_argument('info', type = str, required = True)
+args = parse.parse_args()
+
+
+

+

列表[string]

+

使用参数action = 'append'

+
    +
  • 请求
  • +
+
+ +
+
1
+2
+3
+4
+5
+6
+
+
{
+  "username": "kuari",
+  "info": [
+    "handsome", "cheerful", "optimism"
+  ]
+}
+
+
+
    +
  • 解析
  • +
+
+ +
+
1
+2
+3
+4
+
+
parse = reqparse.RequestParser()
+parse.add_argument('username', type = str, required = True)
+parse.add_argument('info', type = str, action = 'append', required = True)
+args = parse.parse_args()
+
+
+

+

列表[dict]

+

使用参数action = 'append'

+
    +
  • 请求
  • +
+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
{
+  "username": "kuari",
+  "friends": [
+    {
+      "username": "tom",
+      "age": 20
+    },
+    {
+      "username": "jerry",
+      "age": 20
+    }
+  ]
+}
+
+
+
    +
  • 解析
  • +
+
+ +
+
1
+2
+3
+4
+
+
parse = reqparse.RequestParser()
+parse.add_argument('username', type = str, required = True)
+parse.add_argument('info', type = dict, action = 'append', required = True)
+args = parse.parse_args()
+
+
+

+

JSON

+
    +
  • 请求
  • +
+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
{
+  "username": "kuari",
+  "info": {
+    "character": "optimism",
+    "age": 20
+  }
+}
+
+
+
    +
  • 解析
  • +
+
+ +
+
1
+2
+3
+4
+
+
parse = reqparse.RequestParser()
+parse.add_argument('username', type = str, required = True)
+parse.add_argument('info', type = dict, required = True)
+args = parse.parse_args()
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/flask_restful\351\231\220\345\210\266request\345\255\227\346\256\265\351\225\277\345\272\246/index.html" "b/p/flask_restful\351\231\220\345\210\266request\345\255\227\346\256\265\351\225\277\345\272\246/index.html" new file mode 100644 index 0000000..98d629f --- /dev/null +++ "b/p/flask_restful\351\231\220\345\210\266request\345\255\227\346\256\265\351\225\277\345\272\246/index.html" @@ -0,0 +1,728 @@ + + + + +flask_restful限制request字段长度 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ flask_restful限制request字段长度 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

当前产品遇到一个报错,就是接口收到请求没有限制请求字段长度,导致字段长度超过数据库对应字段长度,直接报了500。因此也对此有些新的需求,需要在后端限制请求字段最大长度。

+

环境

+
    +
  • 开发语言:Python 3.7
  • +
  • 后端框架:Flask 1.1.1
  • +
  • 插件:Flask-RESTful 0.3.8
  • +
+

实现

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
def field_max_limit(max_length):
+    def validate(s):
+        if type(s) != str:
+            raise ValidationError("The field must be String.")
+
+        if len(s) <= max_length:
+            return s
+        raise ValidationError("The field cannot exceed %i characters." % max_length)
+
+    return validate
+
+# 解析请求参数时候验证长度
+parse.add_argument('username', type = field_max_limit(5), required = True)
+
+
+

示例

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+
+
from flask import Flask
+from flask_restful import Api, Resource, reqparse
+from werkzeug.routing import ValidationError
+
+app = Flask(__name__)
+api = Api(app)
+
+
+def field_max_limit(max_length):
+    def validate(s):
+        if type(s) != str:
+            raise ValidationError("The field must be String.")
+
+        if len(s) <= max_length:
+            return s
+        raise ValidationError("The field cannot exceed %i characters." % max_length)
+
+    return validate
+
+
+class Login(Resource):
+
+    def post(self):
+        parse = reqparse.RequestParser()
+        parse.add_argument('username', type = field_max_limit(5), required = True)
+        parse.add_argument('password', type = field_max_limit(20), required = True)
+        args = parse.parse_args()
+
+        print({
+            'username': args.username,
+            'password': args.password
+        })
+
+        return {
+            'code': 20000
+        }
+
+
+api.add_resource(Login, '/login')
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/flask_sqlalchemy\344\270\200\345\257\271\344\270\200\344\270\200\345\257\271\345\244\232\345\244\232\345\257\271\344\270\200\345\244\232\345\257\271\345\244\232/index.html" "b/p/flask_sqlalchemy\344\270\200\345\257\271\344\270\200\344\270\200\345\257\271\345\244\232\345\244\232\345\257\271\344\270\200\345\244\232\345\257\271\345\244\232/index.html" new file mode 100644 index 0000000..6295805 --- /dev/null +++ "b/p/flask_sqlalchemy\344\270\200\345\257\271\344\270\200\344\270\200\345\257\271\345\244\232\345\244\232\345\257\271\344\270\200\345\244\232\345\257\271\345\244\232/index.html" @@ -0,0 +1,709 @@ + + + + +flask_sqlalchemy一对一、一对多、多对一、多对多 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ flask_sqlalchemy一对一、一对多、多对一、多对多 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

一对多

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
class Country(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(30), unique=True)
+    capital = db.relationship('Capital', uselist=False)
+
+class Capital(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(30), unique=True)
+    country_id = db.Column(db.Integer, db.ForeignKey('country.id'))
+    country = db.relationship('Country')
+
+
+

一对多

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
class Author(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(70), unique=True)
+    phone = db.Column(db.String(20))
+
+class Article(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    title = db.Column(db.String(50), index=True)
+    body =	db.Column(db.Text)
+
+
+

多对一

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
class Citizen(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(70), unique=True)
+    city_id = db.Column(db.Integer, db.ForeignKey('city.id'))
+    city = db.relationship('City')
+
+class City(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(30), unique=True)
+
+
+

多对多

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
association_table = db.Table('association',db.Column('student_id', db.Integer, db.Foreign)
+
+class Student(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(70), unique=True)
+    grade = db.Column(db.String(20))
+    teachers = db.relationship('Teacher',
+    secondary=association_table, back_populates='students')
+
+class Teacher(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(70), unique=True)
+    office = db.Column(db.String(20))
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/flask_sqlalchemy\346\212\245\351\224\231mysql-server-has-gone-away\350\247\243\345\206\263/index.html" "b/p/flask_sqlalchemy\346\212\245\351\224\231mysql-server-has-gone-away\350\247\243\345\206\263/index.html" new file mode 100644 index 0000000..e2c932d --- /dev/null +++ "b/p/flask_sqlalchemy\346\212\245\351\224\231mysql-server-has-gone-away\350\247\243\345\206\263/index.html" @@ -0,0 +1,638 @@ + + + + +Flask_sqlalchemy报错MySQL server has gone away解决 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Flask_sqlalchemy报错MySQL server has gone away解决 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

报错

+

使用flask_sqlalchemy,服务端出现500错误,日志显示报错如下:

+
+ +
+
1
+
+
MySQL server has gone away
+
+
+

解决

+

添加配置:

+
+ +
+
1
+
+
SQLALCHEMY_POOL_RECYCLE = 280
+
+
+

该配置作用是设置多少秒后回收连接,如果不提供值,默认为 2 小时。此处将其设置为280秒。

+

该配置原文解释:

+
+

Number of seconds after which a connection is automatically recycled. This is required for MySQL, which removes connections after 8 hours idle by default. Note that Flask-SQLAlchemy automatically sets this to 2 hours if MySQL is used.

+
+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/flask_sqlalchemy\347\232\204\345\242\236\345\210\240\346\224\271\346\237\245/index.html" "b/p/flask_sqlalchemy\347\232\204\345\242\236\345\210\240\346\224\271\346\237\245/index.html" new file mode 100644 index 0000000..bca3c37 --- /dev/null +++ "b/p/flask_sqlalchemy\347\232\204\345\242\236\345\210\240\346\224\271\346\237\245/index.html" @@ -0,0 +1,742 @@ + + + + +Flask_sqlalchemy的增删改查 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Flask_sqlalchemy的增删改查 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+
+ +
+
1
+2
+3
+
+
article = Article(title='article1', content='heihei')
+db.session.add(article)
+db.session.commit()
+
+
+

若要在新增之后获取数据库中新增数据的信息,如id

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
article = Article(title='article1', content='heihei')
+db.session.add(article)
+db.session.flush() # 添加这一条,用于预提交
+db.session.commit()
+
+# 输出新增数据的信息
+print(article.id)
+print(article.title)
+
+
+

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
##把需要删除的数据查找出来
+article = Article.query.filter_by(content = 'heihei').first()
+
+##把这条数据删除掉
+db.session.delete(article)
+
+##提交
+db.session.commit()
+
+
+

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
##先把你要更改的数据查找出来
+article = Article.query.filter(Article.title == 'article1').first()
+
+##把这条数据需要修改的地方进行修改
+article.title = 'article2'
+
+##提交
+db.session.commit()
+
+
+

+

查询单个

+
+ +
+
1
+2
+3
+4
+5
+
+
article1 = Article.query.filter(Article.title == 'article').first()
+article1 = Article.query.filter_by(title = 'article').first() # 或者
+
+print(article1.title)
+print(article1.content)
+
+
+

查询所有

+
+ +
+
1
+2
+3
+4
+
+
article1 = Article.query.filter_by(title = 'article').all()
+for item in article1:
+  print(item.title)
+  print(item.content)
+
+
+

倒叙

+
+ +
+
1
+
+
article1 = Article.query.filter_by(title = 'article').order_by(Article.id.desc()).all()
+
+
+

限制数量

+
+ +
+
1
+
+
article1 = Article.query.filter_by(title = 'article').limit(10).all()
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/flask\350\256\276\347\275\256\345\205\250\345\261\200\351\224\231\350\257\257\346\215\225\350\216\267/index.html" "b/p/flask\350\256\276\347\275\256\345\205\250\345\261\200\351\224\231\350\257\257\346\215\225\350\216\267/index.html" new file mode 100644 index 0000000..6c98bf4 --- /dev/null +++ "b/p/flask\350\256\276\347\275\256\345\205\250\345\261\200\351\224\231\350\257\257\346\215\225\350\216\267/index.html" @@ -0,0 +1,657 @@ + + + + +Flask设置全局错误捕获 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Flask设置全局错误捕获 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

代码运行过程中,意外情况会导致500错误,对于使用者来说体验很不好,对于开发者来说也无法及时获取错误,需要去查看日志。

+

并且有的插件在某些报错情况下会返回一些敏感信息,非常危险。因此需要去捕获全局错误,通知开发者,自定义错误消息等。

+

实现

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
+
from server import app
+from flask import request
+from datetime import datetime
+from werkzeug.exceptions import HTTPException
+
+
+@app.errorhandler(Exception)
+def all_exception_handler(e):
+    if isinstance(e, HTTPException):
+        if e.code == 404:
+            return {
+                       'code':    40004,
+                       'message': '404'
+                   }, 404
+
+    # 通知开发者/写入日志
+    handle(path = request.path, content = str(e))
+    
+    return {
+        'code':    20001,
+        'message': 'Error'
+    }
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/gin\344\270\255\351\227\264\344\273\266\345\222\214\351\211\264\346\235\203/index.html" "b/p/gin\344\270\255\351\227\264\344\273\266\345\222\214\351\211\264\346\235\203/index.html" new file mode 100644 index 0000000..3bd7d36 --- /dev/null +++ "b/p/gin\344\270\255\351\227\264\344\273\266\345\222\214\351\211\264\346\235\203/index.html" @@ -0,0 +1,887 @@ + + + + +gin中间件和鉴权 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ gin中间件和鉴权 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

gin的中间件的使用场景非常广泛,此处主要介绍如何使用其来完成常见场景下的鉴权。

+

官方文档

+

官方文档列出了如下几种使用方式:

+ +

不同场景的鉴权实现

+

api key

+

对于api key的方式需要设置白名单,对白名单外的请求进行token检测。此中间件在处理请求被处理之前对请求进行拦截,验证token,因此可在此处利用gin.Context来设置上下文,如请求所属用户的用户信息等。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+
+
package middleware
+
+import (
+	"fmt"
+	"net/url"
+	"strings"
+
+	"github.com/gin-gonic/gin"
+)
+
+func whiteList() map[string]string {
+	return map[string]string{
+		"/ping": "GET",
+	}
+}
+
+func withinWhiteList(url *url.URL, method string) bool {
+	target := whiteList()
+	queryUrl := strings.Split(fmt.Sprint(url), "?")[0]
+	if _, ok := target[queryUrl]; ok {
+		if target[queryUrl] == method {
+			return true
+		}
+		return false
+	}
+	return false
+}
+
+func Authorize() gin.HandlerFunc {
+	return func(c *gin.Context) {
+
+		type QueryToken struct {
+			Token string `binding:"required,len=3" form:"token"`
+		}
+
+		// 当路由不在白名单内时进行token检测
+		if !withinWhiteList(c.Request.URL, c.Request.Method) {
+			var queryToken QueryToken
+			if c.ShouldBindQuery(&queryToken) != nil {
+				c.AbortWithStatusJSON(200, gin.H{
+					"code": 40001,
+				})
+				return
+			}
+
+			c.Set("role", "user")
+		}
+
+		c.Next()
+	}
+}
+
+
+

路由权限

+

1)说明

+

对于请求的处理,需要去验证是否对其请求的路径拥有访问权限。

+

首先看一下gin的路由设置:

+
+ +
+
1
+
+
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes
+
+
+

其参数为...HandlerFunc,其解释为:

+
+ +
+
1
+2
+
+
type HandlerFunc func(*Context)
+HandlerFunc defines the handler used by gin middleware as return value.
+
+
+

所以此处可以通过定制中间件的方式实现一个路由权限处理。

+

当然此处的权限处理比较简单,使用角色直接去判断权限。如分为两个角色,管理员admin和普通用户user

+

不过此处实现有个前提条件,就是如何拿到用户的角色呢?此处需要在上一步(api key)的实现中加上利用gin.Context设置角色:

+
+ +
+
1
+
+
c.Set("role", "admin") // 可见上一步的代码,当然此处只是为了演示设置固定值
+
+
+

然后在中间件中拿到角色并进行判断。

+

2)路由权限中间件

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+
+
package middleware
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/gin-gonic/gin"
+)
+
+func Permissions(roles []string) gin.HandlerFunc {
+	return func(c *gin.Context) {
+
+		permissionsErr := func() error {
+
+			// 获取上下文中的用户角色
+			roleValue, exists := c.Get("role")
+			if !exists {
+				return errors.New("获取用户信息失败")
+			}
+			role := fmt.Sprint(roleValue)
+
+			// 判断请求的用户的角色是否属于设定角色
+			noAccess := true
+			for i := 0; i < len(roles); i++ {
+				if role == roles[i] {
+					noAccess = false
+				}
+			}
+			if noAccess {
+				return errors.New("权限不够")
+			}
+
+			return nil
+
+		}()
+		if permissionsErr != nil {
+			c.AbortWithStatusJSON(200, gin.H{
+				"code": 40001,
+			})
+			return
+		}
+
+		c.Next()
+	}
+}
+
+
+

3)使用

+

在设置路由时候,添加该中间件,并设置白名单。

+
+ +
+
1
+2
+
+
r.POST("/todo", middleware.Permissions([]string{"admin"}), views.AddTodo) // 添加中间件将会验证角色
+r.PUT("/todo", views.ModifyTodo) // 未添加中间件则不会验证角色
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/gin\347\232\204http\345\215\225\345\205\203\346\265\213\350\257\225/index.html" "b/p/gin\347\232\204http\345\215\225\345\205\203\346\265\213\350\257\225/index.html" new file mode 100644 index 0000000..65d37eb --- /dev/null +++ "b/p/gin\347\232\204http\345\215\225\345\205\203\346\265\213\350\257\225/index.html" @@ -0,0 +1,955 @@ + + + + +gin的http单元测试 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ gin的http单元测试 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

Go 标准库专门提供了 net/http/httptest 包专门用于进行 http Web 开发测试。

+

此处基于gin来实现http的单元测试。

+

GET请求

+

此处使用http单元测试对/ping请求测试,其正常会返回字符串pong,响应码为200

+

web应用

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
package main
+
+func setupRouter() *gin.Engine {
+	r := gin.Default()
+	r.GET("/ping", func(c *gin.Context) {
+		c.String(200, "pong")
+	})
+	return r
+}
+
+func main() {
+	r := setupRouter()
+	r.Run(":8080")
+}
+
+
+

http单元测试

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
package main
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPingRoute(t *testing.T) {
+	router := setupRouter()
+
+  // 创建http server并发起请求
+	w := httptest.NewRecorder()
+	req, _ := http.NewRequest("GET", "/ping", nil)
+	router.ServeHTTP(w, req)
+
+	assert.Equal(t, 200, w.Code) // 断言响应码为200
+	assert.Equal(t, "pong", w.Body.String()) // 断言响应为"pong"
+}
+
+
+

POST/PUT/DELETE请求

+

post、put、delete请求处理相似,都是处理其request body,因此此处只以post为例。

+

此处对/todo进行测试,其正常返回为json,其中code20000,响应码为200

+

web应用

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+
+
package main
+
+func setupRouter() *gin.Engine {
+	r := gin.Default()
+	r.POST("/todo", func(c *gin.Context) {
+		type ToDo struct {
+		TodoId int `binding:"required" json:"todo_id"`
+    }
+
+    var todo ToDo
+    if err := c.ShouldBindJSON(&todo); err != nil {
+      c.JSON(400, gin.H{
+        "message": "参数不全",
+      })
+      return
+    }
+    
+    // 处理代码,此处以打印为例,省略处理...
+    fmt.Println(todo.TodoId)
+    
+    c.JSON(200, gin.H{
+			"code": 20000,
+		})
+    
+	})
+	return r
+}
+
+func main() {
+	r := setupRouter()
+	r.Run(":8080")
+}
+
+
+

http单元测试

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+
+
package main
+
+func TestTodoCreate(t *testing.T) {
+  
+  // 请求方法
+	method := "POST"
+  // 请求路由
+	urlStr := "/todo"
+	// request body
+	body := map[string]interface{}{
+		"title": "hello",
+	}
+
+	jsonByte, _ := json.Marshal(body)
+	req := httptest.NewRequest(method, tc.urlStr, bytes.NewReader(jsonByte))
+	w := httptest.NewRecorder()
+	router := routers.SetupRouter()
+	router.ServeHTTP(w, req)
+
+	assert.Equal(t, 200, w.Code) // 判断响应码
+
+	var response map[string]int
+	json.Unmarshal([]byte(w.Body.String()), &response)
+
+	value, exits := response["code"]
+	assert.True(t, exits)
+	assert.Equal(t, 20000, value) // 判断自定义状态码
+}
+
+
+

封装测试请求

+

由于http单元测试代码中存在较多重复,因此此处封装重复代码。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
+
package tests
+
+type TestConfig struct {
+	Url    string
+	Method string
+	Body   interface{}
+}
+
+func (tc *TestConfig) Request() *httptest.ResponseRecorder {
+
+	var req *http.Request
+	if tc.Body != nil {
+		jsonByte, _ := json.Marshal(tc.Body)
+		req = httptest.NewRequest(tc.Method, tc.Url, bytes.NewReader(jsonByte))
+	} else {
+		req = httptest.NewRequest(tc.Method, tc.Url, nil)
+	}
+
+	w := httptest.NewRecorder()
+	router := routers.SetupRouter()
+	router.ServeHTTP(w, req)
+	return w
+}
+
+
+

封装后的单元测试

+

此处以post请求为例。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
package main
+
+func TestAddTag(t *testing.T) {
+	// 新增tag
+	var addTestConfig tests.TestConfig
+	addTestConfig.Method = "POST"
+	addTestConfig.Url = "/tag"
+	addTestConfig.Body = map[string]string{
+		"tag_name": "HelloHello",
+	}
+
+	w := addTestConfig.Request()
+	assert.Equal(t, 200, w.Code)
+
+	var addResponse map[string]int
+	json.Unmarshal([]byte(w.Body.String()), &addResponse)
+
+	value, exits := addResponse["code"]
+	assert.True(t, exits)
+	assert.Equal(t, 20000, value)
+
+	lastInsertTagId, exits := addResponse["tag_id"]
+	assert.True(t, exits)
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/go-http\350\257\267\346\261\202\346\212\245\351\224\231x509-certificate-signed-by-unknown-authority/index.html" "b/p/go-http\350\257\267\346\261\202\346\212\245\351\224\231x509-certificate-signed-by-unknown-authority/index.html" new file mode 100644 index 0000000..15768dd --- /dev/null +++ "b/p/go-http\350\257\267\346\261\202\346\212\245\351\224\231x509-certificate-signed-by-unknown-authority/index.html" @@ -0,0 +1,717 @@ + + + + +Go http请求报错x509 certificate signed by unknown authority + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Go http请求报错x509 certificate signed by unknown authority +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

报错

+

在Go中POST请求时报错

+
+ +
+
1
+
+
x509: certificate signed by unknown authority
+
+
+

即无法检验证书。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+
+
package main
+
+import (
+	"net/http"
+)
+
+func Handle() {
+  
+  ...
+  
+  _, err := http.Post(
+		...
+  )
+  
+  ...
+  
+}
+
+
+

解决

+

跳过校验即可。此处引入"crypto/tls"

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+
+
package main
+
+import (
+  "crypto/tls"
+	"net/http"
+)
+
+func Handle() {
+  
+  ...
+  
+  tr := &http.Transport{
+		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+	}
+
+	client := &http.Client{
+		Timeout:   15 * time.Second,
+		Transport: tr,
+	}
+  
+  _, err := client.Post(
+		...
+  )
+  
+  ...
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/go-redis\347\232\204\345\217\221\345\270\203\344\270\216\350\256\242\351\230\205/index.html" "b/p/go-redis\347\232\204\345\217\221\345\270\203\344\270\216\350\256\242\351\230\205/index.html" new file mode 100644 index 0000000..ff9f367 --- /dev/null +++ "b/p/go-redis\347\232\204\345\217\221\345\270\203\344\270\216\350\256\242\351\230\205/index.html" @@ -0,0 +1,1000 @@ + + + + +go-Redis的发布与订阅 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ go-Redis的发布与订阅 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

在数据量较小的情况下,可以使用Redis来实现消息的发布与订阅,来代替KafkaKafka对于数据量大的场景下性能卓越,但是对于如此小场景时候,不仅运维成本提升,还用不上多少性能。

+

不过使用Redis的另一个弊端是消息不能堆积,一旦消费者节点没有消费消息,消息将会丢失。因此需要评估当下场景来选择适合的架构。

+

此处使用go-redis来实现Redis的发布与订阅。

+

官方文档

+

官方文档有较为完整的例子:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+
+
pubsub := rdb.Subscribe(ctx, "mychannel1")
+
+// Wait for confirmation that subscription is created before publishing anything.
+_, err := pubsub.Receive(ctx)
+if err != nil {
+	panic(err)
+}
+
+// Go channel which receives messages.
+ch := pubsub.Channel()
+
+// Publish a message.
+err = rdb.Publish(ctx, "mychannel1", "hello").Err()
+if err != nil {
+	panic(err)
+}
+
+time.AfterFunc(time.Second, func() {
+	// When pubsub is closed channel is closed too.
+	_ = pubsub.Close()
+})
+
+// Consume messages.
+for msg := range ch {
+	fmt.Println(msg.Channel, msg.Payload)
+}
+
+
+

代码实现

+

分步讲解下具体实现代码。

+

连接redis

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
func redisConnect() (rdb *redis.Client) {
+
+	var (
+		redisServer string
+		port        string
+		password    string
+	)
+
+	redisServer = os.Getenv("RedisUrl")
+	port = os.Getenv("RedisPort")
+	password = os.Getenv("RedisPass")
+
+	rdb = redis.NewClient(&redis.Options{
+		Addr:     redisServer + ":" + port,
+		Password: password,
+		DB:       0, // use default DB
+	})
+
+	return
+}
+
+
+

发布消息

+
+ +
+
1
+2
+3
+4
+
+
func pubMessage(channel, msg string) {
+	rdb := redisConnect()
+	rdb.Publish(context.Background(), channel, msg)
+}
+
+
+

订阅消息

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
func subMessage(channel string) {
+	rdb := redisConnect()
+	pubsub := rdb.Subscribe(context.Background(), channel)
+	_, err := pubsub.Receive(context.Background())
+	if err != nil {
+		panic(err)
+	}
+
+	ch := pubsub.Channel()
+	for msg := range ch {
+		fmt.Println(msg.Channel, msg.Payload)
+	}
+}
+
+
+

完整案例

+

此处分为一个发布节点和一个订阅节点来实现了简单的发布与订阅。

+

消息发布节点

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+
+
package main
+
+import (
+	"context"
+	"fmt"
+	"os"
+
+	"github.com/go-redis/redis/v8"
+)
+
+func redisConnect() (rdb *redis.Client) {
+
+	var (
+		redisServer string
+		port        string
+		password    string
+	)
+
+	redisServer = os.Getenv("RedisUrl")
+	port = os.Getenv("RedisPort")
+	password = os.Getenv("RedisPass")
+
+	rdb = redis.NewClient(&redis.Options{
+		Addr:     redisServer + ":" + port,
+		Password: password,
+		DB:       0, // use default DB
+	})
+
+	return
+}
+
+func pubMessage(channel, msg string) {
+	rdb := redisConnect()
+	rdb.Publish(context.Background(), channel, msg)
+}
+
+func main() {
+	channel := "hello"
+	msgList := []string{"hello", "world"}
+
+  // 此处发了两个消息
+	for _, msg := range msgList {
+		pubMessage(channel, msg)
+		fmt.Printf("已经发送%s到%s\n", msg, channel)
+	}
+}
+
+
+

消息订阅节点

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+
+
package main
+
+import (
+	"context"
+	"fmt"
+	"os"
+
+	"github.com/go-redis/redis/v8"
+)
+
+func redisConnect() (rdb *redis.Client) {
+
+	var (
+		redisServer string
+		port        string
+		password    string
+	)
+
+	redisServer = os.Getenv("RedisUrl")
+	port = os.Getenv("RedisPort")
+	password = os.Getenv("RedisPass")
+
+	rdb = redis.NewClient(&redis.Options{
+		Addr:     redisServer + ":" + port,
+		Password: password,
+		DB:       0, // use default DB
+	})
+
+	return
+}
+
+func subMessage(channel string) {
+	rdb := redisConnect()
+	pubsub := rdb.Subscribe(context.Background(), channel)
+	_, err := pubsub.Receive(context.Background())
+	if err != nil {
+		panic(err)
+	}
+
+	ch := pubsub.Channel()
+	for msg := range ch {
+		fmt.Println(msg.Channel, msg.Payload)
+	}
+}
+
+func main() {
+    channel := "hello"
+    subMessage(channel)
+}
+
+
+

运行结果

+

消息发布节点输出

+ +

消息订阅节点输出

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/golang-aes-256-cbc\345\212\240\345\257\206\345\222\214\350\247\243\345\257\206/index.html" "b/p/golang-aes-256-cbc\345\212\240\345\257\206\345\222\214\350\247\243\345\257\206/index.html" new file mode 100644 index 0000000..9b93f66 --- /dev/null +++ "b/p/golang-aes-256-cbc\345\212\240\345\257\206\345\222\214\350\247\243\345\257\206/index.html" @@ -0,0 +1,825 @@ + + + + +Golang AES-256-CBC加密和解密 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Golang AES-256-CBC加密和解密 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

项目开发中遇到该问题,网上的文章太乱,为了节省下次踩坑时间,特此记录。

+

加解密

+

填充函数

+

该函数在加解密中都需要用到。

+
+ +
+
1
+2
+3
+4
+5
+
+
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padText := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padText...)
+}
+
+
+

加密

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
func Ase256Encrypt(plaintext string, key string, iv string, blockSize int) string {
+	bKey := []byte(key)
+	bIV := []byte(iv)
+	bPlaintext := PKCS5Padding([]byte(plaintext), blockSize)
+	block, _ := aes.NewCipher(bKey)
+	ciphertext := make([]byte, len(bPlaintext))
+	mode := cipher.NewCBCEncrypter(block, bIV)
+	mode.CryptBlocks(ciphertext, bPlaintext)
+
+	return base64.StdEncoding.EncodeToString(ciphertext)
+}
+
+
+

解密

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
func Aes256Decrypt(cryptData, key, iv string) ([]byte, error) {
+	ciphertext, err := base64.StdEncoding.DecodeString(cryptData)
+	if err != nil {
+		return nil, err
+	}
+
+	block, err := aes.NewCipher([]byte(key))
+	if err != nil {
+		return nil, err
+	}
+
+	if len(ciphertext)%aes.BlockSize != 0 {
+		err = errors.New("ciphertext is not a multiple of the block size")
+		return nil, err
+	}
+
+	mode := cipher.NewCBCDecrypter(block, []byte(iv))
+	mode.CryptBlocks(ciphertext, ciphertext)
+
+	return ciphertext, err
+}
+
+
+

全部代码

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+
+
// 填充
+func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padText := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padText...)
+}
+
+// 加密
+func Ase256(plaintext string, key string, iv string, blockSize int) string {
+	bKey := []byte(key)
+	bIV := []byte(iv)
+	bPlaintext := PKCS5Padding([]byte(plaintext), blockSize)
+	block, _ := aes.NewCipher(bKey)
+	ciphertext := make([]byte, len(bPlaintext))
+	mode := cipher.NewCBCEncrypter(block, bIV)
+	mode.CryptBlocks(ciphertext, bPlaintext)
+
+	return base64.StdEncoding.EncodeToString(ciphertext)
+}
+
+// 解密
+func Aes256Decrypt(cryptData, key, iv string) ([]byte, error) {
+	ciphertext, err := base64.StdEncoding.DecodeString(cryptData)
+	if err != nil {
+		return nil, err
+	}
+
+	block, err := aes.NewCipher([]byte(key))
+	if err != nil {
+		return nil, err
+	}
+
+	if len(ciphertext)%aes.BlockSize != 0 {
+		err = errors.New("ciphertext is not a multiple of the block size")
+		return nil, err
+	}
+
+	mode := cipher.NewCBCDecrypter(block, []byte(iv))
+	mode.CryptBlocks(ciphertext, ciphertext)
+
+	return ciphertext, err
+}
+
+
+

调用

+

加密

+
+ +
+
1
+
+
result := Ase256Encrypt("<需要加密的数据>", "<key>", "<iv>", aes.BlockSize)
+
+
+

解密

+
+ +
+
1
+
+
result, err := Aes256Decrypt("<需要解密的数据>", "<key>", "<iv>")
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/golang\344\275\277\347\224\250json\346\240\274\345\274\217\345\255\230\345\217\226redis/index.html" "b/p/golang\344\275\277\347\224\250json\346\240\274\345\274\217\345\255\230\345\217\226redis/index.html" new file mode 100644 index 0000000..b22e556 --- /dev/null +++ "b/p/golang\344\275\277\347\224\250json\346\240\274\345\274\217\345\255\230\345\217\226redis/index.html" @@ -0,0 +1,763 @@ + + + + +Golang使用JSON格式存取Redis + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Golang使用JSON格式存取Redis +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

对于Golang操作Redis,此处使用github.com/go-redis/redis

+

操作

+

连接redis服务器

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+
+
package redis
+
+import (
+	"context"
+	"os"
+	"time"
+
+	"github.com/go-redis/redis/v8"
+	jsoniter "github.com/json-iterator/go"
+)
+
+var ctx = context.Background()
+
+func BaseClient() (rdb *redis.Client) {
+  redisServer := "redis_server"
+  port := "redis_port"
+  password := "redis_password"
+
+	rdb = redis.NewClient(&redis.Options{
+		Addr:     redisServer + ":" + port,
+		Password: password,
+		DB:       0,
+	})
+
+	return
+}
+
+
+

###存储

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
func SetJson(key string, value map[string]interface{}, expiration int) (err error) {
+	rdb := BaseClient()
+	valueString, _ := jsoniter.MarshalToString(value)
+	err = rdb.Set(ctx, key, valueString, time.Duration(expiration)*time.Second).Err()
+	return
+}
+
+
+
    +
  • 调用
  • +
+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
func main() {
+  value, _ := redis.SetJson("user", map[string]interface{}{
+    "name": "tom",
+    "age": 	12
+  }),
+  60,
+}
+
+
+

###读取

+
+ +
+
1
+2
+3
+4
+5
+
+
func Get(key string) (value string, err error) {
+	rdb := BaseClient()
+	value, err = rdb.Get(ctx, key).Result()
+	return
+}
+
+
+
    +
  • 调用
  • +
+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
type User struct {
+  Name	string
+  Age		int
+}
+
+func main() {
+  value, _ := redis.Get("user")
+  
+  var user User
+  json.Unmarshal([]byte(value), &user)
+  
+  fmt.Print(user)
+}
+
+
+

结语

+

使用JSON格式存储与读取其实就是对目标数据在存储前和读取后进行格式转换。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/golang\345\246\202\344\275\225\344\275\277\347\224\250validator\346\240\241\351\252\214\345\217\202\346\225\260/index.html" "b/p/golang\345\246\202\344\275\225\344\275\277\347\224\250validator\346\240\241\351\252\214\345\217\202\346\225\260/index.html" new file mode 100644 index 0000000..97196b4 --- /dev/null +++ "b/p/golang\345\246\202\344\275\225\344\275\277\347\224\250validator\346\240\241\351\252\214\345\217\202\346\225\260/index.html" @@ -0,0 +1,987 @@ + + + + +Golang如何使用validator校验参数 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Golang如何使用validator校验参数 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

关于测试工程师,有一个笑话,是这样的:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
一个测试工程师走进一家酒吧,要了一杯啤酒
+一个测试工程师走进一家酒吧,要了一杯咖啡
+一个测试工程师走进一家酒吧,要了0.7杯啤酒
+一个测试工程师走进一家酒吧,要了-1杯啤酒
+一个测试工程师走进一家酒吧,要了2^32杯啤酒
+一个测试工程师走进一家酒吧,要了一杯洗脚水
+一个测试工程师走进一家酒吧,要了一杯蜥蜴
+一个测试工程师走进一家酒吧,要了一份asdfQwer@24dg!&*(@
+一个测试工程师走进一家酒吧,什么也没要
+一个测试工程师走进家酒吧,又走出去又从窗户进来又从后门出去从下水道钻进来
+一个测试工程师走进家酒吧,又走出去又进来又出去又进来又出去,最后在外面把老板打了一顿
+一个测试工程师走进一
+一个测试工程师走进一家酒吧,要了一杯烫烫烫的锟斤拷
+一个测试工程师走进一家酒吧,要了NaN杯Null
+1T测试工程师冲进一家酒吧,要了500T啤酒咖啡洗脚水野猫狼牙棒奶茶
+1T测试工程师把酒吧拆了
+一个测试工程师化装成老板走进一家酒吧,要了500杯啤酒,并且不付钱
+一万个测试工程师在酒吧外呼啸而过
+一个测试工程师走进一家酒吧,要了一杯啤酒‘;DROPTABLE酒吧
+测试工程师们满意地离开了酒吧
+
+
+

这个笑话估计也只有开发才明白其中的笑点与心酸吧。

+

对于一些刚入门的开发来说,这简直就是噩梦。当初刚入门的时候我的代码也是很多毛病,经不起这样的测试,后来渐渐地经验多了后,代码的健壮性逐渐提升,也明白其中比较重要的就是参数的校验。

+

参数的使用有通过协议的接口调用(如http、rpc……)、函数调用、库调用等等方式。

+

其实对于http api的请求来说,现在很多web框架都已经自带了参数校验的功能,基本用起来都挺爽的,也无需多讲。

+

而对于函数调用这样的常见方式,很多是要靠开发自己去校验参数的。如果仅仅是靠注释,在团队开发过程中,难免会有问题产生。起码我觉得,永远不要相信传过来的参数!

+

OK,那么来讲讲这次的题目,就是Golang中的参数校验库——validator。

+

用过Gin的小伙伴儿应该知道其binding参数验证器非常好用,其就是调用了validator。此处呢我们来介绍下validator的基础用法,和在一般场景下的应用案例。

+

基础用法

+

介绍

+

validator包源码在github.com/go-playground/validator。其基于标记实现结构和单个字段的值验证,包含如下关键功能:

+
    +
  • 使用验证标记或自定义验证程序进行跨字段和跨结构验证
  • +
  • Slice、Array和Map都可以允许验证多维字段的任何或者所有级别
  • +
  • 能够深入查看映射键和值以进行验证
  • +
  • 通过在验证之前确定类型接口的基础类型来处理类型接口
  • +
  • 处理自定义字段类型
  • +
  • 允许将多个验证映射到单个标记,以便在结构上更轻松地定义验证
  • +
  • 提取自定义定义的字段名,例如,可以指定在验证时提取JSON名称,并使其在结果FieldError中可用
  • +
  • 可定制的i18n错误消息
  • +
  • gin web框架的默认验证器
  • +
+

安装

+
+ +
+
1
+
+
go get github.com/go-playground/validator/v10
+
+
+

导入

+
+ +
+
1
+
+
import "github.com/go-playground/validator/v10"
+
+
+

验证规则

+

此处从官方列举的各个类别中挑选部分举例说明。

+

1)比较

+
    +
  • eq:相等
  • +
  • gt:大于
  • +
  • gte:大于等于
  • +
  • lt:小于
  • +
  • lte:小于等于
  • +
  • ne:不等于
  • +
+

2)字段

+

此处的字段大部分可以理解为上面的比较的tag跟field拼接而成,而中间有cs的tag为跨struct比较。

+
    +
  • eqfield(=Field):必须等于Field的值
  • +
  • nefield(=Field):必须不等于Field的值
  • +
  • gtfield(=Field):必须大于Field的值
  • +
  • eqcsfield(=Other.Field):必须等于struct Other中的Field的值
  • +
+

3)网络

+
    +
  • ip:网络协议地址IP
  • +
  • ip4_addr:网络协议地址IPv4
  • +
  • mac:mac地址
  • +
  • url:url
  • +
+

4)字符

+
    +
  • ascii:ASCII
  • +
  • boolean:Boolean
  • +
  • endswith: 以…结尾
  • +
  • contains:包含
  • +
  • uppercase:大写
  • +
+

5)格式

+
    +
  • base64:Base64字符串
  • +
  • base64url:Base64url字符串
  • +
  • email:邮箱字符串
  • +
  • json:JSON
  • +
  • jwt:JSON Web Token
  • +
  • latitude:纬度
  • +
+

6)其它

+
    +
  • len:长度
  • +
  • max:最大值
  • +
  • min:最小值
  • +
  • required:字段为必须,不可空
  • +
+

7)别名

+
    +
  • iscolor:hexcolor|rgb|rgba|hsl|hsla
  • +
  • country_code:iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric
  • +
+

案例

+

简单验证

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+
+
package main
+
+import (
+	"fmt"
+
+	"github.com/go-playground/validator/v10"
+)
+
+type User struct {
+	Name           string  `validate:"required,lte=10"`                         // 姓名 非空,长度小于等于10
+	Age            int     `validate:"required,gte=18,lte=50"`                  // 年龄 非空,数字大于等于18,小于等于50
+	Email          string  `validate:"required,email"`                          // 邮箱 非空,格式为email
+	FavouriteColor string  `validate:"iscolor"`                                 // 喜欢的颜色 hexcolor|rgb|rgba|hsl|hsla的别名
+	Password       string  `validate:"required,gte=16,lte=22"`                  // 密码 非空,长度大于等于16,小于等于22
+	RePassword     string  `validate:"required,gte=16,lte=22,eqfield=Password"` // 确认密码 非空,长度大于等于16,小于等于22,必须和字段Password相同
+	Hobbies        []Hobby `validate:"lte=5"`                                   // 多个爱好 长度小于等于5
+}
+
+type Hobby struct {
+	Name string `validate:"lte=50"` // 爱好名称 长度小于等于50
+}
+
+var validate *validator.Validate
+
+func main() {
+
+	validate = validator.New()
+
+	// 该函数验证struct
+	// 不会报错
+	validateStruct()
+
+	// 该函数单度验证字段
+	// 会报错
+	validateVariable()
+
+}
+
+func validateStruct() {
+
+	hobby := Hobby{
+		Name: "划水",
+	}
+
+	user := User{
+		Name:           "张三",
+		Age:            48,
+		Email:          "hi.hunterji@gmail.com",
+		FavouriteColor: "#ffffff",
+		Password:       "1234567890123456",
+		RePassword:     "1234567890123456",
+		Hobbies:        []Hobby{hobby},
+	}
+
+	err := validate.Struct(user)
+	if err != nil {
+		fmt.Println(err)
+	}
+
+}
+
+func validateVariable() {
+
+	email := "hi.hunterji@gmail.com" // 此处邮箱地址格式写的是错误的,会导致报错
+	err := validate.Var(email, "required,email")
+	if err != nil {
+		fmt.Println(err)
+	}
+
+}
+
+
+

自定义验证

+

自定义验证可以自己创建一个校验的函数:

+
+ +
+
1
+2
+3
+4
+
+
// 注册校验函数
+func ValidateMyVal(fl validator.FieldLevel) bool {
+	return fl.Field().String() == "hello,world!"
+}
+
+
+

然后将其注册到validate上即可:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
validate = validator.New()
+validate.RegisterValidation("is-hello", ValidateMyVal)
+
+s := "hello,kuari" // 跟校验函数中的字符串不同,因此此处会报错
+err := validate.Var(s, "is-hello")
+if err != nil {
+  fmt.Println(err)
+}
+
+
+

自定义校验可以满足开发过程中的特殊场景,通过制定规范的校验标准,可以推进团队的协作和开发效率。

+

最后

+

至此便是对于validator的介绍了。本文篇幅较短,管中窥豹而已,基本可以满足简单场景的使用。以及本文的案例也是基于官方的案例改的,让其稍微接地气点。若有兴趣的小伙伴还是建议去完整看一下官方的文档和案例,多样的用法可以满足多样的场景,在满足代码的健壮性的同时也能确保代码的优美。

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/golang\345\256\236\347\216\260\345\206\234\345\216\206\350\275\254\346\215\242\351\230\263\345\216\206/index.html" "b/p/golang\345\256\236\347\216\260\345\206\234\345\216\206\350\275\254\346\215\242\351\230\263\345\216\206/index.html" new file mode 100644 index 0000000..4f01773 --- /dev/null +++ "b/p/golang\345\256\236\347\216\260\345\206\234\345\216\206\350\275\254\346\215\242\351\230\263\345\216\206/index.html" @@ -0,0 +1,742 @@ + + + + +Golang实现农历转换阳历 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Golang实现农历转换阳历 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

因为项目需求,需要去检测用户的农历生日。虽然后来找到了合适的库,但是首先先解释下农历的定义,也是去了解才知道,原来农历不是阴历。

+

农历属于阴阳合历,其年份分为平年和闰年。平年为十二个月,闰年为十三个月。月份分为大月和小月,大月三十天,小月二十九天,其平均历月等于一个朔望月。

+

环境

+
    +
  • Go 1.16
  • +
  • github.com/nosixtools/solarlunar 0.0.0
  • +
+

+
+ +
+
1
+
+
github.com/nosixtools/solarlunar
+
+
+

该库支持1900~2049年。所以项目要跑到2049年后的童鞋就要注意……

+

当然,该库还支持阳历转农历、节假日计算等,有兴趣大家可以自行去了解下。

+

使用

+

判断闰年

+

该库不支持闰年判断,所以需要自己去实现闰年的判断,其参数类型为Boolean

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
func IsALeapYear(year int) (result bool) {
+	if year%4 == 0 && year%100 != 0 || year%400 == 0 {
+		result = true
+		return
+	}
+	return
+}
+
+
+

转换

+

需要转换的阳历日期格式是固定的,是2006-01-02。此处以农历2021-07-17为例。

+
+ +
+
1
+2
+3
+4
+
+
func main() {
+	lunarDate := "2021-07-17"
+	fmt.Println(solarlunar.LunarToSolar(lunarDate, IsALeapYear(time.Now().Year())))
+}
+
+
+

输出为:

+
+ +
+
1
+
+
2021-08-24
+
+
+

全部代码

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
package main
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/nosixtools/solarlunar"
+)
+
+func main() {
+	lunarDate := "2021-07-17"
+	fmt.Println(solarlunar.LunarToSolar(lunarDate, IsALeapYear(time.Now().Year())))
+}
+
+func IsALeapYear(year int) (result bool) {
+	if year%4 == 0 && year%100 != 0 || year%400 == 0 {
+		result = true
+		return
+	}
+	return
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/go\344\270\255json\350\247\243\346\236\220\346\212\245\351\224\231invalid-character-\\\\b-after-top-level-value/index.html" "b/p/go\344\270\255json\350\247\243\346\236\220\346\212\245\351\224\231invalid-character-\\\\b-after-top-level-value/index.html" new file mode 100644 index 0000000..5416668 --- /dev/null +++ "b/p/go\344\270\255json\350\247\243\346\236\220\346\212\245\351\224\231invalid-character-\\\\b-after-top-level-value/index.html" @@ -0,0 +1,652 @@ + + + + +go中json解析报错invalid character '\\b' after top-level value + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ go中json解析报错invalid character '\\b' after top-level value +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

报错

+

Golangjson解析时报错:

+
+ +
+
1
+
+
invalid character '\\b' after top-level value
+
+
+

代码如下:

+
+ +
+
1
+
+
json.Unmarshal([]byte(result), &response)
+
+
+

分析与排错

+

首先将result打印出来,发现并无异常,其标点符号也没有问题。

+

然后查看网上现有解决方案的帖子基本试了下,起码对于我来说并不适用,概括下方案:

+
    +
  1. 遍历然后过滤,最后重组;
  2. +
  3. 遍历,使用SetEscapeHTML(false)禁用转义符;
  4. +
  5. 编码;
  6. +
  7. +
+

最后对比代码中获取到的字符产长度和手动复制所见的字符串的长度,发现确实代码中字符长度不同,其长度是80,而手动复制的字符串的长度是72。

+

解决

+
+ +
+
1
+
+
strings.ReplaceAll(result, "\b", "")
+
+
+

就挺简单的……

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/go\345\215\225\345\205\203\346\265\213\350\257\225/index.html" "b/p/go\345\215\225\345\205\203\346\265\213\350\257\225/index.html" new file mode 100644 index 0000000..ae82728 --- /dev/null +++ "b/p/go\345\215\225\345\205\203\346\265\213\350\257\225/index.html" @@ -0,0 +1,838 @@ + + + + +go单元测试 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ go单元测试 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

想要写出好的 Go 程序,单元测试是很重要的一部分。 testing 包为提供了编写单元测试所需的工具,写好单元测试后,可以通过 go test 命令运行测试。

+

规则

+

testing 为 Go 语言 package 提供自动化测试的支持。通过 go test 命令,能够自动执行如下形式的任何函数:

+
+ +
+
1
+
+
func TestXxx(*testing.T)
+
+
+

要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 TestXxx 函数,如上所述。 将该文件放在与被测试文件相同的包中。该文件将被排除在正常的程序包之外,但在运行 go test 命令时将被包含。

+

代码结构

+
+ +
+
1
+2
+3
+4
+5
+
+
.
+├── go.mod
+├── intMinBasicDriven_test.go
+├── intMinBasic_test.go
+└── main.go
+
+
+

第一个单元测试

+

要被测试的代码

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
// main.go
+package main
+
+func IntMin(a, b int) int {
+  // 返回a与b中的较小值
+	if a < b {
+		return a
+	} else {
+		return b
+	}
+}
+
+
+

测试代码

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
// intMinBasic_test.go
+package main
+
+import "testing"
+
+func TestIntMinBasic(t *testing.T) {
+	ans := IntMin(2, -2)
+	if ans != -2 {
+    // t.Error* 会报告测试失败的信息,然后继续运行测试。
+    // t.Fail* 会报告测试失败的信息,然后立即终止测试。
+		t.Errorf("IntMin(2, -2) = %d; want -2", ans)
+	}
+}
+
+
+

运行测试

+
+ +
+
1
+2
+3
+
+
go test
+// 输出
+ok  	heihei	0.385s
+
+
+

Table-Driven Test

+

单元测试可以重复,所以会经常使用 表驱动 风格编写单元测试, 表中列出了输入数据,预期输出,使用循环,遍历并执行测试逻辑。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+
+
// intMinBasicDriven_test.go
+package main
+
+import (
+    "fmt"
+    "testing"
+)
+
+
+func TestIntMinTableDriven(t *testing.T) {
+    var tests = []struct {
+        a, b int
+        want int
+    }{
+        {0, 1, 0},
+        {1, 0, 0},
+        {2, -2, -2},
+        {0, -1, -1},
+        {-1, 0, -1},
+    }
+
+    // t.Run 可以运行一个 “subtests” 子测试,一个子测试对应表中一行数据。 运行 go test -v 时,他们会分开显示。
+    for _, tt := range tests {
+
+        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
+        t.Run(testname, func(t *testing.T) {
+            ans := IntMin(tt.a, tt.b)
+            if ans != tt.want {
+                t.Errorf("got %d, want %d", ans, tt.want)
+            }
+        })
+    }
+}
+
+
+

运行代码

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+
+
go test -v
+// 输出
+=== RUN   TestIntMinTableDriven
+=== RUN   TestIntMinTableDriven/0,1
+=== RUN   TestIntMinTableDriven/1,0
+=== RUN   TestIntMinTableDriven/2,-2
+=== RUN   TestIntMinTableDriven/0,-1
+=== RUN   TestIntMinTableDriven/-1,0
+--- PASS: TestIntMinTableDriven (0.00s)
+    --- PASS: TestIntMinTableDriven/0,1 (0.00s)
+    --- PASS: TestIntMinTableDriven/1,0 (0.00s)
+    --- PASS: TestIntMinTableDriven/2,-2 (0.00s)
+    --- PASS: TestIntMinTableDriven/0,-1 (0.00s)
+    --- PASS: TestIntMinTableDriven/-1,0 (0.00s)
+PASS
+ok  	heihei	0.566s
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/go\347\224\237\346\210\2206\344\275\215\351\232\217\346\234\272\346\225\260/index.html" "b/p/go\347\224\237\346\210\2206\344\275\215\351\232\217\346\234\272\346\225\260/index.html" new file mode 100644 index 0000000..404d6e1 --- /dev/null +++ "b/p/go\347\224\237\346\210\2206\344\275\215\351\232\217\346\234\272\346\225\260/index.html" @@ -0,0 +1,597 @@ + + + + +Go生成6位随机数 + + + + + + + + + + + + + + + +
+ + + +
+
+
+ +
+ + +
+

+ Go生成6位随机数 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
package main
+
+import (
+  "math/rand"
+	"strconv"
+)
+
+func CreateVerifyCode() (verifyCode string) {
+	min := 100000
+	max := 999999
+	verifyCode = strconv.Itoa(rand.Intn(max-min) + min)
+	return
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/grafana-loki-docker-driver-client\346\227\245\345\277\227\346\224\266\351\233\206\346\226\271\346\241\210/index.html" "b/p/grafana-loki-docker-driver-client\346\227\245\345\277\227\346\224\266\351\233\206\346\226\271\346\241\210/index.html" new file mode 100644 index 0000000..8aa525e --- /dev/null +++ "b/p/grafana-loki-docker-driver-client\346\227\245\345\277\227\346\224\266\351\233\206\346\226\271\346\241\210/index.html" @@ -0,0 +1,766 @@ + + + + +Grafana+Loki+Docker Driver Client日志收集方案 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Grafana+Loki+Docker Driver Client日志收集方案 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

具体日志采集方案在Grafana+Loki+Promtail日志收集方案文章中已经介绍过,此处不再重复介绍。不太了解的小伙伴儿赶紧去复习!

+

此处主要是记录下Docker Driver Client方式的部署。

+

docker plugin

+

安装

+

安装loki插件。

+
+ +
+
1
+
+
docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions
+
+
+

验证

+
+ +
+
1
+2
+3
+
+
$ docker plugin ls
+ID                  NAME         DESCRIPTION           ENABLED
+ac720b8fcfdb        loki         Loki Logging Driver   true
+
+
+

开启/禁用

+
+ +
+
1
+2
+
+
docker plugin enable loki
+docker plugin disable loki --force
+
+
+

卸载

+
+ +
+
1
+
+
docker plugin rm loki
+
+
+

部署

+

docker-compose.yml

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
version: "3"
+
+networks:
+  loki:
+
+services:
+  loki:
+    image: grafana/loki:2.0.0
+    ports:
+      - "3100:3100"
+    command: -config.file=/etc/loki/local-config.yaml
+    networks:
+      - loki
+
+  grafana:
+    image: grafana/grafana:latest
+    ports:
+      - "3000:3000"
+    networks:
+      - loki
+
+
+

运行

+
+ +
+
1
+
+
docker-compose up -d
+
+
+

容器运行形式

+

容器运行时需要修改log-driver

+
+ +
+
1
+2
+3
+4
+
+
 --log-driver=loki
+--log-opt loki-url="http://<loki-url>/loki/api/v1/push"
+--log-opt loki-retries=5
+--log-opt loki-batch-size=400
+
+
+

举个例子:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
docker run --log-driver=loki \
+    --log-opt loki-url="http://192.168.10.10:3100/loki/api/v1/push" \
+    --log-opt loki-retries=5 \
+    --log-opt loki-batch-size=400 \
+    -p 3000:3000 \
+    nginx
+
+
+

由此查看日志,其labels只有container_name

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/grafana-loki-promtail\346\227\245\345\277\227\346\224\266\351\233\206\346\226\271\346\241\210/index.html" "b/p/grafana-loki-promtail\346\227\245\345\277\227\346\224\266\351\233\206\346\226\271\346\241\210/index.html" new file mode 100644 index 0000000..69707ec --- /dev/null +++ "b/p/grafana-loki-promtail\346\227\245\345\277\227\346\224\266\351\233\206\346\226\271\346\241\210/index.html" @@ -0,0 +1,957 @@ + + + + +Grafana+Loki+Promtail日志收集方案 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Grafana+Loki+Promtail日志收集方案 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

简介

+

Grafana项目由TorkelÖdegaard于2014年发起,最近几年成为GitHub上最受欢迎的开源项目之一。 它使您可以查询,可视化指标并记录警报,无论它们存储在何处。

+

Loki是受Prometheus启发的水平可扩展,高度可用的多租户日志聚合系统。 它被设计为非常经济高效且易于操作。 它不索引日志的内容,而是为每个日志流设置一组标签。

+

Promtail是将本地日志内容发送到私有Loki实例或Grafana Cloud的代理。 通常将其部署到需要监视应用程序的每台机器上。

+

方案对比

+

为什么选择Grafana + Loki + Promtail的日志采集方案呢?

+

我尝试过如下几种方案:

+
    +
  • Elasticsearch+Kibana+Filebeat: 运维成本低,侵入性低,但是对于高并发情况下效果不太好,消耗资源也稍高,需要考虑日志存储成本
  • +
  • Elasticsearch+Kibana+Logstash+Kafka+Filebeat: 可以有效处理高并发情况,且在elk节点挂掉情况下不会丢失日志。但是,运维成本高,需要考虑日志存储成本,整套消耗资源比较高!
  • +
  • Grafana+Loki+Docker Driver Client:使用Docker Driver的方式来直接获取容器的日志,配置较简单,但是需要物理机上安装docker plugin,和运行容器时设置log-driver,侵入性较高
  • +
+

相比之下,当前选择的方案,对于我们当前业务场景下是较为合适的,轻量且侵入性低,由于是检测日志文件,无需担心存储成本。

+

通用日志收集

+

首先介绍下通用日志收集版本的部署。只需要使用默认配置即可收集普通日志,可在http://<your-ip>:3000上查看日志详情。

+

docker-compose

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+
+
version: "3"
+
+networks:
+  loki:
+
+services:
+  loki:
+    image: grafana/loki:2.0.0
+    ports:
+      - "3100:3100"
+    command: -config.file=/etc/loki/local-config.yaml
+    networks:
+      - loki
+
+  promtail:
+    image: grafana/promtail:2.0.0
+    volumes:
+      - /var/log:/var/log
+    command: -config.file=/etc/promtail/config.yml
+    networks:
+      - loki
+
+  grafana:
+    image: grafana/grafana:latest
+    ports:
+      - "3000:3000"
+    networks:
+      - loki
+
+
+

添加数据源

+

部署成功之后,打开http://<your-ip>:3000访问Grafana,在左侧菜单栏选择Configuration,默认进去Data Sources页面。

+

点击Add data sources按钮,选择Loki

+

截屏2021-04-14 下午2.02.57

+

填入URL即可,此处为http://loki:3100,具体要看实际部署。

+

截屏2021-04-14 下午2.03.47

+

然后点击Sace & Test添加。

+

查看与筛选日志

+

在左侧菜单栏选择Explore进入页面,点击左上角的Log brwser按钮,可以查看该数据源的labels,如此处为日志文件。

+

截屏2021-04-14 下午2.08.48

+

在页面顶部的输入框中输入官方的LogQL可以筛选日志。此处日志就不展示了,大家知道有就行了。

+

截屏2021-04-14 下午2.11.09

+

Docker容器日志收集

+

此处详细介绍下关于docker容器日志收集。

+

promtail配置文件

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+
+
##config.yaml
+server:
+  http_listen_address: 0.0.0.0
+  http_listen_port: 9080
+
+positions:
+  filename: /tmp/positions.yaml
+
+clients:
+  - url: http://loki:3100/loki/api/v1/push
+
+scrape_configs:
+- job_name: containers
+  static_configs:
+  - targets:
+      - localhost
+    labels:
+      job: containerlogs
+      __path__: /var/lib/docker/containers/*/*log
+
+  pipeline_stages:
+  - json:
+      expressions:
+        output: log
+        stream: stream
+        attrs:
+  - json:
+      expressions:
+        tag:
+      source: attrs
+  - regex:
+      expression: (?P<container_name>(?:[^|]*[^|]))
+      source: "tag"
+  - timestamp:
+      format: RFC3339Nano
+      source: time
+  - labels:
+      # tag:
+      stream:
+      container_name:
+  - output:
+      source: output
+
+
+

docker-compose

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+
+
version: "3"
+
+networks:
+  loki:
+
+services:
+  loki:
+    image: grafana/loki:2.0.0
+    ports:
+      - "3100:3100"
+    command: -config.file=/etc/loki/local-config.yaml
+    networks:
+      - loki
+
+  promtail:
+    image: grafana/promtail:2.0.0
+    volumes:
+      - /var/lib/docker/containers:/var/lib/docker/containers
+      - ./promtail-config.yaml:/mnt/config/promtail-config.yaml
+    command: -config.file=/mnt/config/promtail-config.yaml
+    networks:
+      - loki
+
+  grafana:
+    image: grafana/grafana:latest
+    ports:
+      - "3000:3000"
+    networks:
+      - loki
+
+
+

容器日志展现形式

+

至此其实就已经可以在Grafana上看到当前容器的日志了,操作如上通用日志采集,但是其展现形式只是filename,也就是类似于107728869f40afa5510879a0e372c77bb513d6154591193d375bfcd421357ed4.log的以container_id展现的日志文件,难以辨认具体是哪个容器。(要是有功夫去登录服务器看下容器id,不如直接看下日志了…)

+

所以,为了能够使用容器名去查看日志,此处需要在容器启动时设置参数:

+
+ +
+
1
+
+
--log-driver json-file --log-opt tag="{{.Name}}"
+
+
+

举个例子:

+
+ +
+
1
+
+
docker run --name hello -p 8080:80 --log-driver json-file --log-opt tag="{{.Name}}" -d nginx
+
+
+

截屏2021-04-14 下午1.55.57

+

对于使用container name,还有另一种方案,就是每次生成container_name和container_id的映射表,个人认为比较麻烦,有兴趣的小伙伴儿可以尝试下。

+

参考文档

+ + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/h5\346\243\200\346\265\213\346\211\213\346\234\272\346\221\207\344\270\200\346\221\207/index.html" "b/p/h5\346\243\200\346\265\213\346\211\213\346\234\272\346\221\207\344\270\200\346\221\207/index.html" new file mode 100644 index 0000000..69084d3 --- /dev/null +++ "b/p/h5\346\243\200\346\265\213\346\211\213\346\234\272\346\221\207\344\270\200\346\221\207/index.html" @@ -0,0 +1,871 @@ + + + + +H5检测手机摇一摇 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ H5检测手机摇一摇 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

要实现h5检测手机摇一摇动作可以直接调用h5原生api。但是在我的实践中发现在ios中限制条件比较多,体验还是有些区别的。

+

如何监听

+

调用Window: devicemotion event即可实现监听。devicemotion事件以固定的时间间隔触发,并指示设备当时在接收的加速物理力量。 它还提供有关旋转速率的信息(如果有)。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
function handleMotionEvent(event) {
+
+    var x = event.accelerationIncludingGravity.x;
+    var y = event.accelerationIncludingGravity.y;
+    var z = event.accelerationIncludingGravity.z;
+
+    // Do something awesome.
+}
+
+window.addEventListener("devicemotion", handleMotionEvent, true);
+
+
+

安卓机

+

安卓机上直接按照如上即可实现。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>测试摇一摇</title>
+</head>
+<body>
+<div class="phone">
+  <div id="show">摇一摇</div>
+</div>
+</body>
+
+<script>
+function handleMotionEvent(event) {
+  document.getElementById('show').innerHTML = '摇动中'
+}
+
+if (window.DeviceMotionEvent) {
+  window.addEventListener("devicemotion", handleMotionEvent, false);
+} else {
+  alert("该浏览器不支持摇一摇功能");
+}
+</script>
+</html>
+
+
+

iPhone

+

限制

+

ios上限制有两条:

+
    +
  • h5必须是https协议的
  • +
  • 必须用户点击授权才可以调用devicemotion
  • +
+

授权

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
function getPermission() {
+  if (
+    typeof window.DeviceMotionEvent !== 'undefined' &&
+    typeof window.DeviceMotionEvent.requestPermission === 'function'
+  ) {
+    window.DeviceMotionEvent.requestPermission()
+      .then(function(state) {
+        if ('granted' === state) {
+          //用户同意授权
+          
+        } else {
+          //用户拒绝授权
+          alert('摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!')
+        }
+      })
+      .catch(function(err) {
+        alert('error: ' + err)
+      })
+  }
+}
+
+
+

直接调用该函数请求授权会导致报错:

+
+ +
+
1
+
+
error: NotAllowedError: Requesting device orientation or motion access requires a user gesture to prompt
+
+
+

需要用户主动去请求授权,因此此处需要将调用放到比如一个按钮上,让用户去点击请求授权。

+
+ +
+
1
+
+
<button onclick="getPermission()">请求授权</button>
+
+
+

全部代码

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+
+
<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>测试摇一摇</title>
+</head>
+<body>
+<div class="phone">
+  <button onclick="getPermission()">请求授权</button>
+  <div id="show"></div>
+</div>
+</body>
+
+<script>
+function handleMotionEvent(event) {
+  document.getElementById('show').innerHTML = '摇动中'
+}
+
+
+function startListen() {
+  if (window.DeviceMotionEvent) {
+    window.addEventListener("devicemotion", handleMotionEvent, false);
+  } else {
+    alert("该浏览器不支持摇一摇功能");
+  }
+}
+
+function getPermission() {
+  if (
+    typeof window.DeviceMotionEvent !== 'undefined' &&
+    typeof window.DeviceMotionEvent.requestPermission === 'function'
+  ) {
+    window.DeviceMotionEvent.requestPermission()
+      .then(function(state) {
+        if ('granted' === state) {
+          //用户同意授权
+          startListen()
+        } else {
+          //用户拒绝授权
+          alert('摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!')
+        }
+      })
+      .catch(function(err) {
+        alert('error: ' + err)
+      })
+  }
+}
+</script>
+</html>
+
+
+
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/interval\350\256\241\346\227\266\345\231\250\345\234\250tab\351\241\265\345\210\207\346\215\242\346\210\226\350\200\205\351\232\220\350\227\217\346\203\205\345\206\265\344\270\213\345\201\234\346\255\242\350\277\220\350\241\214/index.html" "b/p/interval\350\256\241\346\227\266\345\231\250\345\234\250tab\351\241\265\345\210\207\346\215\242\346\210\226\350\200\205\351\232\220\350\227\217\346\203\205\345\206\265\344\270\213\345\201\234\346\255\242\350\277\220\350\241\214/index.html" new file mode 100644 index 0000000..a643d98 --- /dev/null +++ "b/p/interval\350\256\241\346\227\266\345\231\250\345\234\250tab\351\241\265\345\210\207\346\215\242\346\210\226\350\200\205\351\232\220\350\227\217\346\203\205\345\206\265\344\270\213\345\201\234\346\255\242\350\277\220\350\241\214/index.html" @@ -0,0 +1,669 @@ + + + + +Interval计时器在tab页切换或者隐藏情况下停止运行 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Interval计时器在tab页切换或者隐藏情况下停止运行 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

问题

+

开始是开发electron时遇到的问题,使用Interval计时器,在窗口最小化隐藏再打开,计时器在隐藏期间并没有工作。

+

后来网上查询相关问题,发现更多是在浏览器tab页隐藏/切换情况下,计时器就会停止。

+

解决

+
+

在后台选项卡上运行的计时器方法可能会耗尽资源。在后台选项卡中以非常短的时间间隔运行回调的应用程序可能会消耗大量内存,以至于当前活动选项卡的工作可能会受到影响。在最坏的情况下,浏览器可能会崩溃,或者设备的电池会很快耗尽。

+
+

此限制是浏览器限制的。

+

无法突破限制,但是可以使用折中的方式,当然我也觉得此方式相较于一直计时会更优,即监听visibilitychange事件。

+

visibilitychange事件可以监听tab页面的激活与失活事件,因此可以:

+
    +
  • 在失活时,记录计时器计算的最后的值,清空计时器
  • +
  • 在激活时,计算失活期间应有的值,继续使用计时器计算
  • +
+

添加事件代码如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
document.addEventListener('visibilitychange', function() {
+    if(document.hidden) {
+        // tab页失活
+        
+    }
+    else {
+        // tab页激活
+        
+    }
+});
+
+
+

参考文档

+ + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/ios\347\233\221\345\220\254\344\270\212\344\270\213\345\267\246\345\217\263\346\273\221\345\212\250\346\211\213\345\212\277/index.html" "b/p/ios\347\233\221\345\220\254\344\270\212\344\270\213\345\267\246\345\217\263\346\273\221\345\212\250\346\211\213\345\212\277/index.html" new file mode 100644 index 0000000..03db198 --- /dev/null +++ "b/p/ios\347\233\221\345\220\254\344\270\212\344\270\213\345\267\246\345\217\263\346\273\221\345\212\250\346\211\213\345\212\277/index.html" @@ -0,0 +1,665 @@ + + + + +IOS监听上下左右滑动手势 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ IOS监听上下左右滑动手势 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

IOS监听手势使用的方法为UISwipeGestureRecognizer

+

添加手势监听

+
+ +
+
1
+2
+3
+4
+
+
let gesture = UISwipeGestureRecognizer()
+gesture.addTarget(self, action: #selector(yourSelector(gesture:)))
+gesture.direction = .left // .left左滑 .right右滑 .up上滑 .down下滑
+self.addGestureRecognizer(gesture)
+
+
+

添加响应事件

+
+ +
+
1
+2
+3
+
+
@objc private func leftPushEvent(){
+  print("响应...")
+}
+
+
+

模板

+

把上面的整合起来,基本可以按照这个模板来写。

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
@objc private func leftPushEvent(){
+  print("响应...")
+}
+
+let gesture = UISwipeGestureRecognizer()
+gesture.addTarget(self, action: #selector(leftPushEvent(gesture:)))
+gesture.direction = .left
+self.addGestureRecognizer(gesture)
+
+
+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/linux\345\210\206\344\272\253\346\226\207\344\273\266\345\277\253\351\200\237\345\210\233\345\273\272\351\235\231\346\200\201\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250/index.html" "b/p/linux\345\210\206\344\272\253\346\226\207\344\273\266\345\277\253\351\200\237\345\210\233\345\273\272\351\235\231\346\200\201\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250/index.html" new file mode 100644 index 0000000..df9a6f3 --- /dev/null +++ "b/p/linux\345\210\206\344\272\253\346\226\207\344\273\266\345\277\253\351\200\237\345\210\233\345\273\272\351\235\231\346\200\201\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250/index.html" @@ -0,0 +1,750 @@ + + + + +Linux分享文件?快速创建静态文件服务器 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Linux分享文件?快速创建静态文件服务器 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

需求

+

Linux对于开发者来说极其友好,但是由于国内主流办公产品相关的生态较为匮乏,因此如何使用Linux去分享文件是一件十分头疼的问题。

+

对于这个问题,可以直接使用静态文件服务器解决部分需求,如下介绍几个常见方法。

+

语言类

+

Python

+

对于Python来说,可以直接使用内置的库来实现。

+
    +
  • +

    python2

    +
    + +
    +
    1
    +
    +
    python -m SimpleHTTPServer 8000
    +
    +
    +
  • +
  • +

    Python3

    +
    + +
    +
    1
    +
    +
    python -m http.server 8000
    +
    +
    +
  • +
+

Node.js

+

node生态内有一个项目http-server,直接V8引擎带你飞。

+
    +
  1. 安装
  2. +
+
    +
  • Npm
  • +
+
+ +
+
1
+
+
npm install --global http-server
+
+
+
    +
  • Homebrew
  • +
+
+ +
+
1
+
+
brew install http-server
+
+
+
    +
  1. 运行
  2. +
+
+ +
+
1
+
+
http-server [path] [options]
+
+
+

例如:

+
+ +
+
1
+2
+
+
cd exmaple/
+http-server
+
+
+
    +
  1. 项目仓库地址
  2. +
+

https://github.com/http-party/http-server

+

服务类

+
    +
  1. Nginx/Apache
  2. +
+

NginxApache本身可用于静态文件服务器,这就需要用户直接在本地安装。

+

当然,nginx需要注意配置一下,打开索引:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
server {
+	listen	80;
+	...
+	
+	location / {
+		root /usr/share/nginx/html;
+		autoindex on;
+	}
+}
+
+
+
    +
  1. Docker
  2. +
+

使用Docker其实也是使用如Nginx来实现静态文件服务器,但是容器化在该场景存在几大优势:

+
    +
  • 即开即用
  • +
  • 环境隔离
  • +
+

相对于直接安装Nginx或者Apache,更推荐使用Docker

+ +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/nginx\346\212\245\351\224\231an-upstream-response-is-buffered-to-a-temporary-file/index.html" "b/p/nginx\346\212\245\351\224\231an-upstream-response-is-buffered-to-a-temporary-file/index.html" new file mode 100644 index 0000000..66eaed4 --- /dev/null +++ "b/p/nginx\346\212\245\351\224\231an-upstream-response-is-buffered-to-a-temporary-file/index.html" @@ -0,0 +1,621 @@ + + + + +nginx报错an upstream response is buffered to a temporary file + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ nginx报错an upstream response is buffered to a temporary file +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

报错

+
+ +
+
1
+
+
an upstream response is buffered to a temporary file
+
+
+

解决

+

Nginx配置加上如下配置

+
+ +
+
1
+2
+
+
proxy_max_temp_file_size 0;
+client_max_body_size 50m;
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/node-spawn\345\234\250windows\344\270\213\344\270\215\347\224\237\346\225\210\351\227\256\351\242\230\350\256\260\345\275\225/index.html" "b/p/node-spawn\345\234\250windows\344\270\213\344\270\215\347\224\237\346\225\210\351\227\256\351\242\230\350\256\260\345\275\225/index.html" new file mode 100644 index 0000000..c2f2cb3 --- /dev/null +++ "b/p/node-spawn\345\234\250windows\344\270\213\344\270\215\347\224\237\346\225\210\351\227\256\351\242\230\350\256\260\345\275\225/index.html" @@ -0,0 +1,661 @@ + + + + +node spawn在windows下不生效问题记录 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ node spawn在windows下不生效问题记录 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

问题描述

+

使用electron开发的windows桌面应用程序,在调用目标文件夹底下的exe执行文件时,开发机子上没有问题,但是其他机子使用时一直调用失败,也抓取不到日志。

+
+ +
+
1
+2
+3
+4
+5
+
+
spawn(path.join(remote.app.getAppPath(), "../target.exe"), [], {
+  shell: true,
+  detached: false,
+  windowsHide: true
+});
+
+
+

原因

+

路径存在空格。

+

也是经过各种原因排查,然后一次偶然的成功才注意到了路径问题,排查之后发现确实是这问题……

+

解决

+

spawn按照如上我的代码一定条件下可以运行,其有一个参数cwd,用来表明运行目录。spawn第一个参数必须是命令的名字,不能是路径。

+

所以如上代码改成这样:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
spawn("target.exe", [], { // 此处直接写目标exe文件
+  cwd: path.join(remote.app.getAppPath(), "../"), // 注意这里,使用了cwd参数来写运行目录
+  shell: true,
+  detached: false,
+  windowsHide: true
+});
+
+
+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/oss\350\212\261\345\274\217\350\247\243\351\224\201\344\270\213\350\275\275\346\226\207\344\273\266\346\226\260\345\247\277\345\212\277\344\275\240\345\255\246\345\272\237\344\272\206\345\220\227/index.html" "b/p/oss\350\212\261\345\274\217\350\247\243\351\224\201\344\270\213\350\275\275\346\226\207\344\273\266\346\226\260\345\247\277\345\212\277\344\275\240\345\255\246\345\272\237\344\272\206\345\220\227/index.html" new file mode 100644 index 0000000..45e1564 --- /dev/null +++ "b/p/oss\350\212\261\345\274\217\350\247\243\351\224\201\344\270\213\350\275\275\346\226\207\344\273\266\346\226\260\345\247\277\345\212\277\344\275\240\345\255\246\345\272\237\344\272\206\345\220\227/index.html" @@ -0,0 +1,695 @@ + + + + +OSS花式解锁下载文件新姿势,你学废了吗? + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ OSS花式解锁下载文件新姿势,你学废了吗? +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

在上一篇文章——前端文件花式直传OSS!后端:那我走?中聊了下文件上传的几种方案,这里我们再来聊一下文件下载的花式姿势。

+

精简版

+

最常见的方式,莫过于后端存储文件在服务器上,然后通过后端接口传给前端,如下图所示。

+

最简版

+

该方案对于小规模、成本较低的项目非常适用,开发也较为便捷。

+

而对于有性能要求的项目,可以通过砸钱加机器、分片下载等方案提升项目性能。如果可以的话,请砸钱加机器吧!

+截屏2021-09-28 下午9.58.10 +

中庸版

+

相较于上一个方案,可以砸丢丢钱整个OSS,将文件存储在OSS上,毕竟OSS上行流量不收费,如下图所示。

+

中庸版直传

+

那么问题来了,OSS的下行流量不是收费的吗?!

+

OK,偷偷告诉各位一个省钱小妙招/狗头,OSS内网的下行流量是不收费的!因此,可以通过后端请求OSS,获取到文件/字符串后,将其以文件流/base64数据的方式返回给前端。这样就避免了下行流量的费用。如下图所示。

+

中庸版

+

不过问题又来了,这样就还是占用了后端服务器的资源,依然会是性能的一个瓶颈。

+截屏2021-09-28 下午10.12.06 +

性能版

+

基于上一个方案,可以再升级。砸丢丢钱,拉上CDN这老哥,利用CDN流量代替OSS的下行流量,既能让前端直接请求OSS资源,不占用服务器资源,也降低了成本。如图所示。

+

性能版

+

在优先性能的情况下,该方案是较优的。

+

要说缺点的话,就是CDN配置吧,需要买域名和SSL证书等,不过一次购买,后续使用体验会非常棒。CDN除了可以代替OSS的下行流量外,其优点不要太多,比如说CDN可以文件缓存、可以调度至加速节点等。

+

涉及到OSS的私有Bucket的话,只需要使用CDN的访问控制即可。其也只需要通过后端实现加密,生成文件访问URL给前端直接访问。

+

或许你会存在疑问,看上去挺麻烦的啊!但是看看CDN的价格,你肯定会有不一样的想法的。

+截屏2021-09-28 下午10.37.14 +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/promise-inside-request-interceptor/index.html b/p/promise-inside-request-interceptor/index.html new file mode 100644 index 0000000..c44339c --- /dev/null +++ b/p/promise-inside-request-interceptor/index.html @@ -0,0 +1,651 @@ + + + + +Promise inside request interceptor + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Promise inside request interceptor +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

问题

+

在使用axios的拦截器时候,需要在request中调用一个promise函数,因此需要等待其执行完成才能去进行下一步。

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
function getToken() {
+	return new Promise(...)
+}
+
+// Request interceptors
+service.interceptors.request.use(config => {
+  getToken()
+  ...
+})
+
+
+

解决

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
function getToken() {
+	return new Promise(...)
+}
+
+// Request interceptors
+service.interceptors.request.use(async config => {
+  awit getToken()
+  ...
+})
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/python3-socket-tcp-example/index.html b/p/python3-socket-tcp-example/index.html new file mode 100644 index 0000000..d0a53d6 --- /dev/null +++ b/p/python3-socket-tcp-example/index.html @@ -0,0 +1,717 @@ + + + + +python3 socket tcp example + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ python3 socket tcp example +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

socket tcp server

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+
+
import socket
+
+
+def socket_tcp_server(server_ip: str = '0.0.0.0', server_port: int = 9000, buffer_size: int = 1024):
+    """
+    socket tcp 服务端
+
+    :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有
+    :param server_port: 服务器tcp server接收信息的端口, 默认9000
+    :param buffer_size: 套接字缓冲区大小, 默认1024
+    :return: none
+    """
+    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    tcp_socket.bind((server_ip, server_port))
+    tcp_socket.listen(128)
+    print('服务端开始运行...\n')
+
+    while True:
+        client, sender_info = tcp_socket.accept()
+        receive_data = client.recv(buffer_size)
+        print('客户端地址: {}'.format(sender_info))
+        print('来自客户端的信息: {}'.format(receive_data.decode('utf-8')))
+
+        # 返回消息
+        client.send(str.encode('response...'))
+
+
+if __name__ == '__main__':
+    socket_tcp_server()
+
+
+

socket tcp client

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
import socket
+
+
+def socket_tcp_client_send_message(message: str, server_ip: str, server_port: int, buffer_size: int = 1024):
+    """
+    socket tcp 客户端发送消息
+
+    :param message: 消息
+    :param server_ip: 服务端的ip地址
+    :param server_port: 服务端的端口号
+    :return: none
+    """
+    tcp_client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
+    tcp_client_socket.connect((server_ip, server_port))
+    tcp_client_socket.send(str.encode(message))
+
+    response = tcp_client_socket.recv(buffer_size)
+    print('response : {}'.format(response.decode()))
+
+    tcp_client_socket.close()
+
+
+if __name__ == '__main__':
+    socket_tcp_client_send_message('hello,world!', '127.0.0.1', 9000)
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/python3-socket-udp-example/index.html b/p/python3-socket-udp-example/index.html new file mode 100644 index 0000000..947dbfd --- /dev/null +++ b/p/python3-socket-udp-example/index.html @@ -0,0 +1,695 @@ + + + + +python3 socket udp example + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ python3 socket udp example +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

socket udp server

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
import socket
+
+
+def socket_udp_server(server_ip: str = '0.0.0.0', server_port: int = 9000, buffer_size: int = 1024):
+    """
+    socket udp 服务端
+
+    :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有
+    :param server_port: 服务器udp server接收信息的端口, 默认9000
+    :param buffer_size: 套接字缓冲区大小, 默认1024
+    :return: none
+    """
+    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    udp_socket.bind((server_ip, server_port))
+    print('服务端开始运行...\n')
+
+    while True:
+        receive_data, sender_info = udp_socket.recvfrom(buffer_size)
+        print('客户端地址: {}'.format(sender_info))
+        print('来自客户端的信息: {}'.format(receive_data.decode('utf-8')))
+
+
+if __name__ == '__main__':
+    socket_udp_server()
+
+
+

socket udp client

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
+
import socket
+
+
+def socket_udp_client_send_message(message: str, server_ip: str, server_port: int):
+    """
+    socket udp 客户端发送消息
+
+    :param message: 消息
+    :param server_ip: 服务端的ip地址
+    :param server_port: 服务端的端口号
+    :return: none
+    """
+    udp_client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
+    udp_client_socket.sendto(str.encode(message), (server_ip, server_port))
+
+
+if __name__ == '__main__':
+    socket_udp_client_send_message('hello,world!', '127.0.0.1', 9000)
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/sftp\351\203\250\347\275\262\346\212\245\351\224\231\350\247\243\345\206\263\350\256\260\345\275\225/index.html" "b/p/sftp\351\203\250\347\275\262\346\212\245\351\224\231\350\247\243\345\206\263\350\256\260\345\275\225/index.html" new file mode 100644 index 0000000..c8ee268 --- /dev/null +++ "b/p/sftp\351\203\250\347\275\262\346\212\245\351\224\231\350\247\243\345\206\263\350\256\260\345\275\225/index.html" @@ -0,0 +1,691 @@ + + + + +SFTP部署报错解决记录 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SFTP部署报错解决记录 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

部署SFTP服务器,数次遇到几个报错,特此记录

+

环境

+
    +
  • +

    路径

    +
    + +
    +
    1
    +2
    +3
    +
    +
    home
    +└── tom
    +    └── uploads
    +
    +
    +
  • +
  • +

    用户为tom

    +
  • +
+

报错

+

报错一

+
+ +
+
1
+
+
permission denied
+
+
+

报错二

+
+ +
+
1
+
+
bad ownership or modes for chroot directory component "/home"
+
+
+

解决

+

以上两个报错,此处为统一解决。

+
    +
  • 创建用户组
  • +
+
+ +
+
1
+
+
groupadd ftp
+
+
+
    +
  • 将用户加入用户组
  • +
+
+ +
+
1
+
+
usermod -a -G ftp tom
+
+
+
    +
  • 设置权限
  • +
+
+ +
+
1
+2
+3
+
+
chown root:ftp -R /home/tom
+chown tom:ftp -R /home/tom/uploads
+chmod 755 -R /home
+
+
+
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/sqlx\345\273\272\346\250\241\350\277\236\346\216\245\344\270\216\344\275\277\347\224\250/index.html" "b/p/sqlx\345\273\272\346\250\241\350\277\236\346\216\245\344\270\216\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..82f96fd --- /dev/null +++ "b/p/sqlx\345\273\272\346\250\241\350\277\236\346\216\245\344\270\216\344\275\277\347\224\250/index.html" @@ -0,0 +1,1033 @@ + + + + +sqlx建模、连接与使用 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ sqlx建模、连接与使用 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

为什么要用sqlx而不是gorm呢?是因为orm学习成本比较高,当使用python时候需要使用sqlalchemy,遇到go就要换成gorm,换成别的语言就又有其他orm。而直接使用原生sql可以减少学习成本,适用于所有开发语言。其次,gorm本身支持软删除,但是其对软删除的支持上存在缺陷,在单条查询可以过滤软删除数据,但是在多条查询时无法有效过滤,就造成了有时候要手动过滤又有时候不要手动过滤,使用体验非常差。

+

因此此处考虑去使用sqlx来直接调用原生sql

+

安装

+
+ +
+
1
+
+
go get github.com/jmoiron/sqlx
+
+
+

连接数据库

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+
+
package database
+
+import (
+	"fmt"
+	"os"
+
+	_ "github.com/go-sql-driver/mysql"
+	"github.com/jmoiron/sqlx"
+)
+
+func DBConnect() *sqlx.DB {
+	env := os.Getenv("NODE_ENV")
+
+	var host, port, user, password, dbname string
+  // 使用环境变量来切换生产和开发环境
+	if env == "production" {
+		host = os.Getenv("dbHost")
+		port = os.Getenv("dbPort")
+		user = os.Getenv("dbUser")
+		password = os.Getenv("dbPassword")
+		dbname = os.Getenv("dbname")
+	} else {
+		host = "<your-host>"
+		port = "<your-port>"
+		user = "<your-user>"
+		password = "<your-password>"
+		dbname = "<your-db-name>"
+	}
+
+	dbConfig := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", user, password, host, port, dbname)
+	db, err := sqlx.Connect("mysql", dbConfig)
+	if err != nil {
+		fmt.Println(err)
+		panic("failed to connect database")
+	}
+
+	return db
+}
+
+
+

创建表和调用

+

创建表

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
create table if not exists test_gin.todo
+(
+    todo_id    int auto_increment
+        primary key,
+    title      varchar(20)                          not null comment 'todo标题',
+    content    varchar(200)                         null comment '内容',
+    user_id    int                                  not null comment '用户id',
+    created_at timestamp  default CURRENT_TIMESTAMP null comment '创建时间戳',
+    updated_at timestamp  default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间戳',
+    is_deleted tinyint(1) default 0                 not null comment '是否被删除,0:未删,1:已删'
+);
+
+
+

创建struct

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
type Todo struct {
+	TodoID    int    `db:"todo_id" json:"todo_id,omitempty"`
+	Title     string `db:"title" json:"title,omitempty"`
+	Content   string `db:"content" json:"content,omitempty"`
+	UserID    int    `db:"user_id" json:"user_id,omitempty"`
+	CreatedAt string `db:"created_at" json:"created_at,omitempty"`
+	UpdatedAt string `db:"updated_at" json:"updated_at,omitempty"`
+	IsDeleted bool   `db:"is_deleted" json:"is_deleted,omitempty"`
+}
+
+
+

封装方法

+

此处以增删为例。封装常用方法是为了复用,封装时候使用害羞的代码。不需要为了封装而封装。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+
+
// 新增Todo
+func (t *Todo) Add() (todoID int, err error) {
+
+	// 连接数据库
+	db := database.DBConnect()
+	defer db.Close()
+
+	// 执行添加sql
+	tx := db.MustBegin()
+	result := tx.MustExec("insert into todo (title, content, user_id) value (?, ?, ?)",
+		t.Title, t.Content, t.UserID)
+	lastTodoID, err := result.LastInsertId()
+	if err != nil {
+		return
+	}
+	_ = tx.Commit()
+
+	todoID = int(lastTodoID)
+	return
+}
+
+// 删除Todo
+func (t *Todo) Del() {
+
+	db := database.DBConnect()
+	defer db.Close()
+
+	tx := db.MustBegin()
+	tx.MustExec("update todo set is_deleted = 0 where is_deleted = 0 and todo_id = ?", t.TodoID)
+	_ = tx.Commit()
+}
+
+
+

视图中使用

+

此处以新增接口为例。

+

调用封装的方法

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+
+
package views
+
+import (
+	"github.com/gin-gonic/gin"
+	"testGin/models"
+)
+
+// addTodo.go -- post
+
+func AddTodo(c *gin.Context) {
+
+	// {"title": "hello", "content": "world"}
+	var requestBody struct {
+		Title   string `json:"title"`
+		Content string `json:"content"`
+	}
+
+	if c.ShouldBind(&requestBody) != nil {
+		c.JSON(200, gin.H{
+			"code":    40000,
+			"message": "参数有误",
+		})
+		return
+	}
+
+	todo := models.Todo{
+		Title:   requestBody.Title,
+		Content: requestBody.Content,
+		UserID:  1, // 此处的1为假数据,此处应当从上下文获取请求用户的user_id
+	}
+	todoID, err := todo.Add()
+	if err != nil {
+		c.JSON(200, gin.H{
+			"code": 20001,
+		})
+		return
+	}
+
+	c.JSON(200, gin.H{
+		"code":    20000,
+		"todo_id": todoID,
+	})
+}
+
+
+

直接使用

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+
+
package views
+
+import (
+	"github.com/gin-gonic/gin"
+	"testGin/database"
+)
+
+// addTodo.go -- post
+
+func AddTodo(c *gin.Context) {
+
+	// {"title": "hello", "content": "world"}
+	var requestBody struct {
+		Title   string `json:"title"`
+		Content string `json:"content"`
+	}
+
+	if c.ShouldBind(&requestBody) != nil {
+		c.JSON(200, gin.H{
+			"code":    40000,
+			"message": "参数有误",
+		})
+		return
+	}
+
+	// 连接数据库
+	db := database.DBConnect()
+	defer db.Close()
+
+	// 执行添加sql
+	tx := db.MustBegin()
+	result := tx.MustExec("insert into todo (title, content, user_id) value (?, ?, ?)",
+		requestBody.Title, requestBody.Content, 1)
+	lastTodoID, err := result.LastInsertId()
+	if err != nil {
+		c.JSON(200, gin.H{
+			"code": 20001,
+		})
+		return
+	}
+	_ = tx.Commit()
+
+	c.JSON(200, gin.H{
+		"code":    20000,
+		"todo_id": int(lastTodoID),
+	})
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swift-ui\351\241\271\347\233\256\350\260\203\347\224\250core-data/index.html" "b/p/swift-ui\351\241\271\347\233\256\350\260\203\347\224\250core-data/index.html" new file mode 100644 index 0000000..f7b5d9d --- /dev/null +++ "b/p/swift-ui\351\241\271\347\233\256\350\260\203\347\224\250core-data/index.html" @@ -0,0 +1,1038 @@ + + + + +Swift UI项目调用core data + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Swift UI项目调用core data +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

这篇文章是我写的第一篇Swift UI相关的笔记吧。

+

这真的难搞哦,毕竟新出来的。国内文档真的是少之又少,国外的文档也真不多,基本搜出来的都是UIKit的。官方文档,也是一言难尽,基本就处于,我要实现一个功能,然后去搜一下各种帖子,筛选掉无用的帖子,找到有用的点,当然也是时常根本找不到有用的帖子,然后就要去油管上看各种教程,然后发现:哦!原来还有这个方法!接着去官方文档搜一下,看一下属性,自己调用调用……

+

光是这个core data我就折腾了近一周,最后发现,原来还是官方好呀…..

+

吐槽一下自学swift ui,现在进入正题了。

+

core data呢,是苹果官方的本地数据库,但是其存储的文件其实是sqlite文件。其可以通过icloud实现备份和同步,当然icloud我会额外写一篇文档来详细讲述的(又是一把辛酸泪…)。

+

环境

+

如下是我当前的环境:

+
    +
  • 系统:macOS Big Sur 11.4
  • +
  • Xcode:Version 12.5 (12E262)
  • +
  • Swift:5.4
  • +
+

操作步骤

+

创建项目

+

在创建项目的时候,可以直接选择Use Core Data选项,xcode会直接在ContentView.swift中生成一个相关demo。

+

创建项目时候截图

+

查看相关文件

+

创建之后,查看文件目录,相对于不选择Use Core Data,会多出如下几个文件:

+
    +
  • .xcdatamodeId
  • +
  • Persistence.swift
  • +
+

创建core data表

+

点击<Your-Project-Name>.xcdatamodeId文件,进入页面。

+

可以看到页面内有默认的CONFIGURATIONSDefault,默认的ENITITIESItem,可以理解为分别对应sql中的库和表。

+

swiftui_core_data_page.png

+

然后点击Item,可以看到其字段,有默认的timestamp字段。

+

若要新增ENTITIES(表),点击底部的Add Entity按钮即可。

+

若要新增Attributes(字段),点击右侧Attributes中的+即可,注意字段要选择类型。

+

比如,此处以默认的Item为例,新增usernameage两个字段。

+

swiftui_core_data_create_attributes.png

+

代码层面操作core data

+

1)查看

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
@Environment(\.managedObjectContext) private var viewContext
+
+@FetchRequest(
+  sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
+  animation: .default)
+private var items: FetchedResults<Item>
+
+// body中便利items即可
+
+
+

2)新增

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
private func addItem() {
+  withAnimation {
+    let newItem = Item(context: viewContext)
+    newItem.timestamp = Date()
+
+    do {
+      try viewContext.save()
+    } catch {
+      // Replace this implementation with code to handle the error appropriately.
+      // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+      let nsError = error as NSError
+      fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+    }
+  }
+}
+
+
+

如上是xcode自动生成的新增函数,若是要自定义添加字段可以这样改动下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
+
// 此处按照如上添加Attributes修改,具体修改按照项目具体情况
+private func addItem(username: String, age: Int16) {
+  withAnimation {
+    let newItem = Item(context: viewContext)
+    newItem.username = username
+    newItem.age = age
+    newItem.timestamp = Date()
+
+    do {
+      try viewContext.save()
+    } catch {
+      // Replace this implementation with code to handle the error appropriately.
+      // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+      let nsError = error as NSError
+      fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+    }
+  }
+}
+
+
+

3)删除

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
private func deleteItems(offsets: IndexSet) {
+  withAnimation {
+    offsets.map { items[$0] }.forEach(viewContext.delete)
+
+    do {
+      try viewContext.save()
+    } catch {
+      // Replace this implementation with code to handle the error appropriately.
+      // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+      let nsError = error as NSError
+      fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+    }
+  }
+}
+
+
+

4)汇总

+

在默认生成代码中,有一个toolbar,其在macos中可以生效,但是在ios中只有EditionButton()可以使用,为了方便演示,此处新增一个Button来添加数据。

+

其次有点要声明下,在xcode中写代码时,右侧的canvas会实时渲染,列表中出现的数据并不是core data中的数据,而是默认生成的Persistence.swift中生成的演示数据,只能看看,不能当真。只有在模拟器/实体机编译运行时才能操作core data

+

如下为修改过后的ContentView.swift文件:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+
+
//
+//  ContentView.swift
+//  HelloKuari
+//
+//  Created by Kuari on 2021/6/5.
+//
+
+import SwiftUI
+import CoreData
+
+struct ContentView: View {
+    @Environment(\.managedObjectContext) private var viewContext
+
+    @FetchRequest(
+        sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
+        animation: .default)
+    private var items: FetchedResults<Item>
+
+    var body: some View {
+        
+        VStack {
+            List {
+                ForEach(items) { item in
+                    Text("Tom: \(item.username!) age: \(item.age) time :  \(item.timestamp!, formatter: itemFormatter)")
+                }
+                .onDelete(perform: deleteItems)
+            }
+            
+            // 新增一个按钮来添加数据
+            Button(action: {
+                addItem(username: "tom", age: 12)
+            }, label: {
+                Text("Add Item")
+            })
+        }
+
+    }
+
+    private func addItem(username: String, age: Int16) {
+        withAnimation {
+            let newItem = Item(context: viewContext)
+            newItem.username = username
+            newItem.age = age
+            newItem.timestamp = Date()
+
+            do {
+                try viewContext.save()
+            } catch {
+                // Replace this implementation with code to handle the error appropriately.
+                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+                let nsError = error as NSError
+                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+            }
+        }
+    }
+
+    private func deleteItems(offsets: IndexSet) {
+        withAnimation {
+            offsets.map { items[$0] }.forEach(viewContext.delete)
+
+            do {
+                try viewContext.save()
+            } catch {
+                // Replace this implementation with code to handle the error appropriately.
+                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+                let nsError = error as NSError
+                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+            }
+        }
+    }
+}
+
+private let itemFormatter: DateFormatter = {
+    let formatter = DateFormatter()
+    formatter.dateStyle = .short
+    formatter.timeStyle = .medium
+    return formatter
+}()
+
+struct ContentView_Previews: PreviewProvider {
+    static var previews: some View {
+        ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
+    }
+}
+
+
+

然后点击左侧顶部的运行按钮,编译运行。

+

一开始是空白一片,点击Add Item按钮之后,便会开始添加数据。

+swiftui_core_data_add_item_demo.png +

对于记录右滑即可删除,其为ListonDelete方法。

+

结语

+

该文章是面向新手的,也是记录下我踩过的坑,因为目前文档匮乏,身边也没swift的开发小伙伴儿,只能靠自己摸索,若有大佬有更好的方法,真的还请不吝赐教。

+

后面持续记录踩坑中……

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui-macos\351\241\271\347\233\256alert\345\274\271\345\207\272\344\270\244\346\254\241\351\227\256\351\242\230\350\247\243\345\206\263/index.html" "b/p/swiftui-macos\351\241\271\347\233\256alert\345\274\271\345\207\272\344\270\244\346\254\241\351\227\256\351\242\230\350\247\243\345\206\263/index.html" new file mode 100644 index 0000000..3b33804 --- /dev/null +++ "b/p/swiftui-macos\351\241\271\347\233\256alert\345\274\271\345\207\272\344\270\244\346\254\241\351\227\256\351\242\230\350\247\243\345\206\263/index.html" @@ -0,0 +1,661 @@ + + + + +SwiftUI MacOS项目alert弹出两次问题解决 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI MacOS项目alert弹出两次问题解决 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

问题

+

使用Alert时,将其用在list的循环视图元素中,弹出Alert时,一定时长不选择就会在点击后弹出第二次。

+

这里提一下就是之前在网上看到一个帖子说他将Alert放在NavigationView上也会出现该问题。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
+
        VStask {
+            ForEach(items, id: \.self) { item in
+                ElementView(item: item) // 循环中的元素
+                    .alert(isPresented: $showAlert) {
+                        Alert(
+                            title: Text("删除确认"),
+                            message: Text("请问您确认删除该数据吗?"),
+                            primaryButton: .default(
+                                Text("取消"),
+                                action: {
+                                    showAlert = false
+                                }
+                            ),
+                            secondaryButton: .destructive(
+                                Text("删除"),
+                                action: {
+                                    deleteItems(offsets: [index])
+                                })
+                        )
+                    }
+            }
+        }
+
+
+

解决

+

Alert放到循环之前的元素上,比如VStackList

+

参考

+ + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui-macos\351\241\271\347\233\256\346\240\271\346\215\256\345\261\217\345\271\225\345\244\247\345\260\217\350\260\203\346\225\264\347\252\227\345\217\243\345\244\247\345\260\217/index.html" "b/p/swiftui-macos\351\241\271\347\233\256\346\240\271\346\215\256\345\261\217\345\271\225\345\244\247\345\260\217\350\260\203\346\225\264\347\252\227\345\217\243\345\244\247\345\260\217/index.html" new file mode 100644 index 0000000..dc2efe6 --- /dev/null +++ "b/p/swiftui-macos\351\241\271\347\233\256\346\240\271\346\215\256\345\261\217\345\271\225\345\244\247\345\260\217\350\260\203\346\225\264\347\252\227\345\217\243\345\244\247\345\260\217/index.html" @@ -0,0 +1,650 @@ + + + + +SwiftUI MacOS项目根据屏幕大小调整窗口大小 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI MacOS项目根据屏幕大小调整窗口大小 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

代码实现

+

获取屏幕对象

+
+ +
+
1
+
+
var window = NSScreen.main?.visibleFrame
+
+
+

设置大小

+
+ +
+
1
+2
+3
+4
+
+
HStack {
+  
+}
+.frame(width: window!.width / 2.0, height: window!.height / 1.5)
+
+
+

汇总

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
struct Home: View {
+
+	var window = NSScreen.main?.visibleFrame
+  
+  var body: some View {
+    HStack {
+      Text("Hello, World!")
+    }
+    .frame(width: window!.width / 2.0, height: window!.height / 1.5)
+  }
+}
+
+
+
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui-reality\345\274\200\345\217\221ar\351\241\271\347\233\256\350\247\243\345\206\263\345\205\250\345\261\217\351\227\256\351\242\230/index.html" "b/p/swiftui-reality\345\274\200\345\217\221ar\351\241\271\347\233\256\350\247\243\345\206\263\345\205\250\345\261\217\351\227\256\351\242\230/index.html" new file mode 100644 index 0000000..8aa338a --- /dev/null +++ "b/p/swiftui-reality\345\274\200\345\217\221ar\351\241\271\347\233\256\350\247\243\345\206\263\345\205\250\345\261\217\351\227\256\351\242\230/index.html" @@ -0,0 +1,632 @@ + + + + +SwiftUI+Reality开发AR项目解决全屏问题 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI+Reality开发AR项目解决全屏问题 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

环境

+
    +
  • Swift 5.4
  • +
  • Xcode 12.5.1
  • +
+

问题

+

在使用Swift UIRealiy开发AR项目时,发现摄像头一直是居中的,无法全屏。

+

解决

+

创建LaunchScreen.storyboard文件

+

在左侧文件列表中新建文件,名为LaunchScreen.storyboard

+

设置Launch Screen File

+

点击左侧文件列表中你的项目文件(最顶级文件),进入文件[your-project].xcodeproj文件。

+

General中,找到App Icons and Launch Images,在其模块中有Launch Screen File选项,点击选择为LaunchScreen.storyboard

+

总结下就是:[yourTarget] -> General -> App Icons and Launch Images

+

参考文档

+ + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui\351\241\271\347\233\256image\347\202\271\345\207\273\344\272\213\344\273\266/index.html" "b/p/swiftui\351\241\271\347\233\256image\347\202\271\345\207\273\344\272\213\344\273\266/index.html" new file mode 100644 index 0000000..29030f0 --- /dev/null +++ "b/p/swiftui\351\241\271\347\233\256image\347\202\271\345\207\273\344\272\213\344\273\266/index.html" @@ -0,0 +1,665 @@ + + + + +SwiftUI项目Image点击事件 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI项目Image点击事件 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

swiftui中的点击可以有两种情况:

+
    +
  • Button
  • +
  • Gestures
  • +
+

根据不同情况可以去不同地使用。

+

单纯的按钮

+

此处单纯的按钮即为有按钮样式和点击事件。

+
+ +
+
1
+2
+3
+4
+5
+
+
Button(action: {
+  ... // 点击事件触发的代码
+}, label: {
+	Image(systemName: "plus")
+})
+
+
+

无样式的按钮

+

即为没有按钮样式的按钮,方便直接展示Image

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
Button(action: {
+  ... // 点击事件触发的代码
+}, label: {
+	Image(systemName: "plus")
+})
+.buttonStyle(BorderlessButtonStyle())
+
+
+

TapGesture事件

+
+ +
+
1
+2
+3
+4
+
+
Image(systemName: "plus")
+  .onTapGesture {
+    ... // 点击事件触发的代码
+  }
+
+
+
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui\351\241\271\347\233\256\345\210\244\346\226\255\346\230\257\345\220\246\344\270\272\346\232\227\351\273\221\346\250\241\345\274\217/index.html" "b/p/swiftui\351\241\271\347\233\256\345\210\244\346\226\255\346\230\257\345\220\246\344\270\272\346\232\227\351\273\221\346\250\241\345\274\217/index.html" new file mode 100644 index 0000000..951f9bc --- /dev/null +++ "b/p/swiftui\351\241\271\347\233\256\345\210\244\346\226\255\346\230\257\345\220\246\344\270\272\346\232\227\351\273\221\346\250\241\345\274\217/index.html" @@ -0,0 +1,669 @@ + + + + +SwiftUI项目判断是否为暗黑模式 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI项目判断是否为暗黑模式 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

实现

+
+ +
+
1
+2
+3
+4
+5
+
+
@Environment(\.colorScheme) var colorScheme
+
+var isLight: Bool {
+  colorScheme == .light
+}
+
+
+

调用

+
+ +
+
1
+2
+
+
Text("Hello, World !")
+	.foregroundColor(isLight ? Color.red : Color.green)
+
+
+

完整例子

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
import SwiftUI
+
+struct CheckIsLight: View {
+    
+    @Environment(\.colorScheme) var colorScheme
+
+    var isLight: Bool {
+      colorScheme == .light
+    }
+    
+    var body: some View {
+        Text("Hello, World !")
+            .foregroundColor(isLight ? Color.red : Color.green) // 此处使用isLght实现根据暗黑模式切换字体颜色
+    }
+}
+
+struct CheckIsLight_Previews: PreviewProvider {
+    static var previews: some View {
+        CheckIsLight()
+    }
+}
+
+
+
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui\351\241\271\347\233\256\345\244\215\345\210\266\345\255\227\347\254\246\344\270\262\345\210\260\345\211\252\345\210\207\346\235\277/index.html" "b/p/swiftui\351\241\271\347\233\256\345\244\215\345\210\266\345\255\227\347\254\246\344\270\262\345\210\260\345\211\252\345\210\207\346\235\277/index.html" new file mode 100644 index 0000000..bfb18f8 --- /dev/null +++ "b/p/swiftui\351\241\271\347\233\256\345\244\215\345\210\266\345\255\227\347\254\246\344\270\262\345\210\260\345\211\252\345\210\207\346\235\277/index.html" @@ -0,0 +1,657 @@ + + + + +SwiftUI项目复制字符串到剪切板 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI项目复制字符串到剪切板 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

这是个比较坑的问题,我一开始开发的是macos项目,到网上搜的方案基本都是使用UIPasteboard方法,但是偏偏用不了。

+

后来开发ios项目,用macos的就不行,发现UIPasteboard的可行,所以这里需要清楚的是,ios和macos的复制方法是不同的……

+

MacOS

+

实现

+
+ +
+
1
+2
+3
+4
+5
+
+
func copyToClipBoard(textToCopy: String) {
+  let pasteBoard = NSPasteboard.general
+  pasteBoard.clearContents()
+  pasteBoard.setString(textToCopy, forType: .string)
+}
+
+
+

调用

+
+ +
+
1
+
+
copyToClipBoard(textToCopy: "Hello,World!")
+
+
+

IOS

+

实现

+
+ +
+
1
+
+
UIPasteboard.general.setValue(<Your-String>, forPasteboardType: kUTTypePlainText as String)
+
+
+

调用

+
+ +
+
1
+
+
UIPasteboard.general.setValue("Hello,World!", forPasteboardType: kUTTypePlainText as String)
+
+
+
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui\351\241\271\347\233\256\345\256\236\347\216\260\346\220\234\347\264\242\345\212\237\350\203\275/index.html" "b/p/swiftui\351\241\271\347\233\256\345\256\236\347\216\260\346\220\234\347\264\242\345\212\237\350\203\275/index.html" new file mode 100644 index 0000000..a8601d1 --- /dev/null +++ "b/p/swiftui\351\241\271\347\233\256\345\256\236\347\216\260\346\220\234\347\264\242\345\212\237\350\203\275/index.html" @@ -0,0 +1,662 @@ + + + + +SwiftUI项目实现搜索功能 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI项目实现搜索功能 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

实现

+

创建变量

+
+ +
+
1
+
+
@State var search: String = ""
+
+
+

过滤

+

此处过滤条件为判断元素是否包含搜索的文本。

+
+ +
+
1
+
+
<Your-Array>.filter({"\($0)".contains(search.lowercased()) || search.isEmpty})
+
+
+

汇总

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
+
struct DataList: View {
+
+  @State var search: String = ""
+  @Binding var dataList: [Item]
+
+  var dataSearchFilterList: [Item] {
+    dataList.filter({"\($0)".contains(search.lowercased()) || search.isEmpty})
+  }
+
+  var body: some View {
+    if dataSearchFilterList.isEmpty {
+      Text("搜索不到...")
+    } else {
+      ... // 展示搜索结果
+    }
+  }
+  
+}
+
+
+
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swiftui\351\241\271\347\233\256\350\260\203\347\224\250\347\224\237\347\211\251\350\257\206\345\210\253touch-id--face-id/index.html" "b/p/swiftui\351\241\271\347\233\256\350\260\203\347\224\250\347\224\237\347\211\251\350\257\206\345\210\253touch-id--face-id/index.html" new file mode 100644 index 0000000..bc13314 --- /dev/null +++ "b/p/swiftui\351\241\271\347\233\256\350\260\203\347\224\250\347\224\237\347\211\251\350\257\206\345\210\253touch-id--face-id/index.html" @@ -0,0 +1,722 @@ + + + + +SwiftUI项目调用生物识别(Touch ID -- Face ID) + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ SwiftUI项目调用生物识别(Touch ID -- Face ID) +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

设置权限

+

打开文件info.plist,在空白处右击,选择Add Row,输入选择Privacy - Face ID Usage Description,然后在value中写入我们需要验证您的身份以保护数据

+

swiftui_face_privacy_info_plist.png

+

代码层面接入

+

打开ContentView.swift文件,开始如下操作。

+

引入相关库

+
+ +
+
1
+
+
import LocalAuthentication
+
+
+

创建lock变量

+
+ +
+
1
+
+
@State private var isUnlocked = false
+
+
+

isUnlocked为是否解锁,true表示验证完成,已解锁,false表示验证失败,未解锁。

+

创建函数

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
func authenticate() {
+    let context = LAContext()
+    var error: NSError?
+
+    // 检查生物特征认证是否可用
+    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
+        // 可用,所以继续使用它
+        let reason = "我们需要验证您的身份以保护数据"
+
+        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
+            // 身份验证现已完成
+            DispatchQueue.main.async {
+                if success {
+                    // 认证成功,解锁
+                    self.isUnlocked = true
+                } else {
+                    // 发生的异常
+                }
+            }
+        }
+    } else {
+        // 没有生物识别
+    }
+}
+
+
+

根据isUnlocked切换View

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
VStack {
+    if self.isUnlocked {
+        Text("Unlocked")
+    } else {
+        Text("Locked")
+    }
+}
+.onAppear(perform: authenticate) // 该方法调用生物识别验证函数
+
+
+

审核问题

+

最近提交了ios和macos两个产品到app store,我设置的强制使用生物识别才能进入应用。但是出现的问题是macos的审核过了,而ios的审核没有过,其反馈的问题即为开始的生物识别没有过,审核人员使用的模拟器,根本不存在生物识别。

+

所以跟可能会踩这个坑的小伙伴儿提个醒,目前我还没有好的解决方案,正在等待新的审核中……

+ +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/swift\350\256\241\347\256\227\344\270\244\344\270\252\346\227\245\346\234\237\347\232\204\345\244\251\346\225\260\345\267\256/index.html" "b/p/swift\350\256\241\347\256\227\344\270\244\344\270\252\346\227\245\346\234\237\347\232\204\345\244\251\346\225\260\345\267\256/index.html" new file mode 100644 index 0000000..d65f325 --- /dev/null +++ "b/p/swift\350\256\241\347\256\227\344\270\244\344\270\252\346\227\245\346\234\237\347\232\204\345\244\251\346\225\260\345\267\256/index.html" @@ -0,0 +1,693 @@ + + + + +Swift计算两个日期的天数差 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Swift计算两个日期的天数差 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

官方方法

+

DateComponents

+

A date or time specified in terms of units (such as year, month, day, hour, and minute) to be evaluated in a calendar system and time zone.

+

以要在日历系统和时区中计算的单位(例如年、月、日、小时和分钟)指定的日期或时间。

+

实现

+

计算两个字符串形式的日期的天数差

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
func dateDiff() -> Int {
+  // 计算两个日期差,返回相差天数
+  let formatter = DateFormatter()
+  let calendar = Calendar.current
+  formatter.dateFormat = "yyyy-MM-dd"
+  let today = Date()
+  
+  // 开始日期
+  let startDate = formatter.date(from: "2021-06-08")
+  
+  // 结束日期
+  let endDate = formatter.date(from: "2021-06-09")
+  let diff:DateComponents = calendar.dateComponents([.day], from: startDate!, to: endDate!)
+  return diff.day!
+}
+
+
+

计算当天跟某一天的天数差

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+
+
func checkDiff() -> Int {
+  // 计算两个日期差,返回相差天数
+  let formatter = DateFormatter()
+  let calendar = Calendar.current
+  formatter.dateFormat = "yyyy-MM-dd"
+
+  // 当天
+  let today = Date()
+  let startDate = formatter.date(from: formatter.string(from: today))
+  
+  // 固定日期
+  let endDate = formatter.date(from: "2021-06-09")
+  
+  let diff:DateComponents = calendar.dateComponents([.day], from: startDate!, to: endDate!)
+  return diff.day!
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/tailwindcss\345\237\272\347\241\200/index.html" "b/p/tailwindcss\345\237\272\347\241\200/index.html" new file mode 100644 index 0000000..4aeabe2 --- /dev/null +++ "b/p/tailwindcss\345\237\272\347\241\200/index.html" @@ -0,0 +1,965 @@ + + + + +tailwindcss基础 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ tailwindcss基础 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+
+

Rapidly build modern websites without ever leaving your HTML.

+
+

Tailwind CSS可以快速建立现代网站,而无需离开HTML。其特性是原子化,很像的BootStrap的css。

+

通俗点解释就是,其封装了很多独立的css样式,只需要在html中添加class即可调用,而不需要去从头写css样式。

+

安装

+

下载包

+
+ +
+
1
+
+
npm install tailwindcss@latest postcss@latest autoprefixer@latest
+
+
+

可能会遇到如下报错:

+
+ +
+
1
+
+
Error: PostCSS plugin tailwindcss requires PostCSS 8.
+
+
+

那就需要降低PostCSS的版本。如下,先卸载,再去安装。

+
+ +
+
1
+2
+
+
npm uninstall tailwindcss postcss autoprefixer
+npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
+
+
+

添加Tailwind作为PostCSS插件

+

添加tailwindcssautoprefixerPostCSS配置。大部分情况下作为postcss.config.js文件放在项目的顶级路径下。其也能作为.postcssrc文件,或者使用postcss键放在package.json文件中。

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
// postcss.config.js
+module.exports = {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  }
+}
+
+
+

创建配置文件

+

如果想自定义安装,当使用npm安装tailwindcss时候需要使用tailwind命令行去生成一个配置文件。

+
+ +
+
1
+
+
npx tailwindcss init
+
+
+

这将会创建一个最小化的tailwind.config.js文件,其位于项目的顶级路径下。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
// tailwind.config.js
+module.exports = {
+  purge: [],
+  darkMode: false, // or 'media' or 'class'
+  theme: {
+    extend: {},
+  },
+  variants: {},
+  plugins: [],
+}
+
+
+

在CSS中包含Tailwind

+

创建styles.css文件。

+
+ +
+
1
+2
+3
+4
+
+
/* ./your-css-folder/styles.css */
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+
+

引入该文件。

+
+ +
+
1
+
+
import "./styles.css"
+
+
+

构建CSS

+

为生产而构建时,确保配置清除选项以删除任何最小文件大小的未使用类。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
// tailwind.config.js
+module.exports = {
+  purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], // 修改此行
+  darkMode: false, // or 'media' or 'class'
+  theme: {
+    extend: {}
+  },
+  variants: {
+    extend: {}
+  },
+  plugins: []
+};
+
+
+

简要说明

+

由于其样式属性巨多,此处只举几例作简要说明,讲解基础用法。在开始不熟悉的情况下,要开着其手册查询。

+

Width

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Class解释
w-0width: 0px;
w-1width: 0.25rem;
w-1/2width: 50%;
w-fullwidth: 100%;
+
+ +
+
1
+2
+3
+4
+
+
<!--示例-->
+<div>
+  <div class="w-1/2"></div>
+</div>
+
+
+

Padding

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Class解释
p-0padding: 0px;
p-5padding: 1.25rem;
pl-1padding-left: 0.25rem;
+
+ +
+
1
+2
+
+
<!--示例-->
+<div class="p-5"></div>
+
+
+

Position

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Class解释
staticposition: static;
fixedposition: fixed;
absoluteposition: absolute;
+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
<!--示例-->
+<div class="static">
+  <p>Static parent</p>
+  <div class="absolute bottom-0 left-0 ...">
+    <p>Absolute child</p>
+  </div>
+</div>
+
+
+

Flex垂直居中

+
+ +
+
1
+2
+3
+4
+5
+
+
<div class="flex flex-row justify-center items-center">
+  <div>1</div>
+  <div>2</div>
+  <div></div>
+</div>
+
+
+

简单案例

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<div class="flex flex-col justify-center items-center p-20">
+  <div v-for="item in 10"
+       :key="item"
+       class="flex flex-row justify-between items-center w-1/5 bg-gray-100 m-5 p-10 cursor-pointer shadow rounded hover:shadow-lg transition duration-300 ease-in-out">
+    <img src="@/assets/message.png" alt="logo" height="50px" width="50px">
+    <div class="flex flex-col ml-5">
+      <div class="text-lg">今天晚上加{{ item }}个鸡腿</div>
+      <div class="text-sm text-gray-500">2020.2.{{ item }}</div>
+    </div>
+  </div>
+</div>
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/uniapp-canvas\347\224\237\346\210\220\346\265\267\346\212\245\345\212\237\350\203\275\346\213\206\350\247\243\345\222\214\351\227\256\351\242\230\350\256\260\345\275\225/index.html" "b/p/uniapp-canvas\347\224\237\346\210\220\346\265\267\346\212\245\345\212\237\350\203\275\346\213\206\350\247\243\345\222\214\351\227\256\351\242\230\350\256\260\345\275\225/index.html" new file mode 100644 index 0000000..6883aec --- /dev/null +++ "b/p/uniapp-canvas\347\224\237\346\210\220\346\265\267\346\212\245\345\212\237\350\203\275\346\213\206\350\247\243\345\222\214\351\227\256\351\242\230\350\256\260\345\275\225/index.html" @@ -0,0 +1,1093 @@ + + + + +uniapp canvas生成海报功能拆解和问题记录 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ uniapp canvas生成海报功能拆解和问题记录 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

最近在用uniapp开发小程序,需要用到canvas画海报然后再保存本地。

+

之前写过同样功能的文章,不过场景不同,之前是在web上生成海报,该场景可以使用之前文章的方法——html转canvas来实现。

+

但是uniapp则不同,该框架是去DOM化的,因此只能使用uniapp的官方canvas来实现。

+

功能拆解

+

网上找到的文章,有几篇写得挺好的,展现了完整的功能。这里我把用到的几个功能拆解出来,而不用先通读整篇代码。

+

创建canvas

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+
+
<template>
+	<view>
+		<canvas style="width: 300px; height: 200px;" canvas-id="firstCanvas" id="firstCanvas"></canvas>
+  </view>
+</template>
+
+<script>
+export default {
+  onReady() {
+    // 初始化
+    const ctx = uni.createCanvasContext('firstCanvas')
+    
+    // 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
+    ctx.draw()
+  }
+}
+</script>
+
+
+

背景色

+
+ +
+
1
+2
+3
+
+
ctx.setFillStyle('red')
+ctx.fillRect(0, 0, 300, 200)
+// 此处其实绘制了一个300x200的红色矩形,但是其大小跟canvas大小相同即为背景色了
+
+
+

加载图片

+

1)本地图片

+

图片直接在项目中,可以直接加载。

+
+ +
+
1
+
+
ctx.drawImage("./background.png", 0, 0, 300, 200)
+
+
+

2)url

+

此处url返回的为文件流,在uniapp中无法直接加载,需要转换成本地信息才可以使用。

+

有两种方式可以使用,小程序都需要添加download合法域名:

+
    +
  • uni.getImageInfo:获取文件信息,我使用的这个方法
  • +
  • uni.downloadFile:下载文件
  • +
+

原生使用方法是这样的:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
uni.getImageInfo({
+  src: url,
+  success: (res) => {
+    ctx.drawImage(res.path, 0, 0, 300, 200)
+  }
+})
+
+
+

由于js的异步问题,如果图片较大或者多个图片的情况下,会有这边还没加载完,canvas就已经绘制完了的情况,所以这里将其优化下。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
// 首先封装
+const getImageInfo = (url: string): Promise<string> => {
+  return new Promise((req, rej) => {
+    uni.getImageInfo({
+      src: url,
+      success: (res) => {
+        req(res.path)
+      }
+    })
+  })
+}
+
+// 调用
+const genPoster = async() => {
+  const imgPath = await getImageInfo(<your-url>) // 建议所有图片在开始绘制canvas前加载好
+
+  const ctx = uni.createCanvasContext('firstCanvas')
+	ctx.drawImage(imgPath, 0, 0, 300, 200)
+  ctx.draw()
+}
+
+
+

3)base64

+

如果你的图片数据是base64的,那恭喜你,依然加载不了。当然这存在的情况是,微信开发工具是没有问题的,但是上了真机之后直接无法加载了,这波是小程序的锅。

+

这里呢需要将图片存储然后用本地地址绘制。

+

原生方法:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
const fs = wx.getFileSystemManager()
+let times = new Date().getTime()
+let codeImg = wx.env.USER_DATA_PATH + '/' + times + '.png'
+return new Promise((req, rej) => {
+  fs.writeFile({
+    filePath: imgPath,
+    data: <your-base64-data>,
+    encoding: 'base64',
+    success: () => {
+      ctx.drawImage(imgPath, 0, 0, 300, 200)
+    }
+  })
+})
+
+
+

优化一波:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
+
// 封装
+const getBase64ImageInfo = (base64Data: string): Promise<string> => {
+  const fs = wx.getFileSystemManager()
+  let times = new Date().getTime()
+  let codeImg = wx.env.USER_DATA_PATH + '/' + times + '.png'
+  return new Promise((req, rej) => {
+    fs.writeFile({
+      filePath: imgPath,
+      data: base64Data,
+      encoding: 'base64',
+      success: () => {
+        req(imgPath)
+      }
+    })
+  })
+}
+
+// 调用
+const genPoster = async() => {
+  const imgPath = await getBase64ImageInfo(<your-base64-data>) // 建议所有图片在开始绘制canvas前加载好
+
+  const ctx = uni.createCanvasContext('firstCanvas')
+	ctx.drawImage(imgPath, 0, 0, 300, 200)
+  ctx.draw()
+}
+
+
+

文字

+
+ +
+
1
+2
+3
+4
+
+
ctx.setFontSize(13)
+ctx.font = "nomarl bold 13px Arial,sans-serif" // 加粗等功能
+ctx.setFillStyle('#ffffff')
+ctx.fillText("hello, world !", 16, 16)
+
+
+

圆角矩形

+

想要绘制一个圆角的矩形,啊……这波就复杂了,原理就不细讲了,直接上代码,调用即可。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
const roundedRect = (x: number, y: number, width: number, height: number, radius: number) => {
+  if (width <= 0 || height <= 0) {
+    ctx.arc(x, y, radius, 0, Math.PI * 2);
+    return;
+  }
+
+  ctx.moveTo(x + radius, y);
+  ctx.arcTo(x + width, y, x + width, y + height, radius);
+  ctx.arcTo(x + width, y + height, x, y + height, radius);
+  ctx.arcTo(x, y + height, x, y, radius);
+  ctx.arcTo(x, y, x + radius, y, radius);
+}
+
+const drawRoundedRect = (strokeStyle: string, fillStyle: string, x: number, y: number, width: number, height: number, radius: number) => {
+  ctx.beginPath();
+  roundedRect(x, y, width, height, radius);
+  ctx.strokeStyle = strokeStyle;
+  ctx.fillStyle = fillStyle;
+  ctx.stroke();
+  ctx.fill();
+}
+
+// 调用
+drawRoundedRect('#ffffff', '#ffffff', 16, 16, 86, 6)
+
+
+

图片加载为圆形

+

基本原理是,正常加载图片,canvas画个圆给它裁剪掉,上代码!

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
ctx.save()
+ctx.beginPath()
+ctx.arc(16, 16, 12, 0, 2 * Math.PI)
+// 如果小伙伴儿调试时候感觉圆形和图片有点错位,可以开启下面两行注释代码,给圆圈加个边框
+// ctx.setStrokeStyle('#AAAAAA')
+// ctx.stroke()
+ctx.clip()
+ctx.drawImage(<your-image-path>, 16, 16, 24, 24)
+ctx.restore()
+
+
+

canvas生成的海报下载

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+
+
const savePoster = () => {
+  uni.showModal({
+    title: '提示',
+    content: '确定保存到相册吗',
+    success: (response) => {
+      uni.canvasToTempFilePath({
+        canvasId: 'sharePoster',
+        success: (response) => {
+          uni.saveImageToPhotosAlbum({
+            filePath: response.tempFilePath,
+            success: (response) => {
+              console.log(response);
+              // 此处为执行成功
+              // ...
+            },
+            fail: (response) => {
+              uni.openSetting({
+                success: (response) => {
+                  if (!response.authSetting['scope.writePhotosAlbum']) {
+                    uni.showModal({
+                      title: '提示',
+                      content: '获取权限成功,再次点击图片即可保存',
+                      showCancel: false
+                    })
+                  } else {
+                    uni.showModal({
+                      title: '提示',
+                      content: '获取权限失败,无法保存',
+                      showCancel: false
+                    })
+                  }
+                }
+              })
+            }
+          })
+        },
+        fail: (response) => {
+          console.log(response);
+        }
+      }, this);
+    }
+  })
+}
+
+
+

问题

+

图片有时显示有时不显示

+
+ +
+
1
+
+
参照本文“二.3.加载图片”优化代码处,将加载图片全部写成同步的,在开始绘制前将图片全都加载好。
+
+
+

base64数据的图片在小程序开发工具显示,到了真机就不显示了

+
+ +
+
1
+
+
参照本文“二.3.3)base64”优化代码处,使用该方法即可。小程序canvas无法直接加载base64图片。
+
+
+

canvas整体画成圆角的

+
+ +
+
1
+
+
canvas背景是透明色,只要画个大小覆盖canvas的圆角矩形或者使用圆角背景图即可。
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/v-charts\346\267\273\345\212\240\345\233\276\350\241\250\346\240\207\351\242\230/index.html" "b/p/v-charts\346\267\273\345\212\240\345\233\276\350\241\250\346\240\207\351\242\230/index.html" new file mode 100644 index 0000000..b9b1a19 --- /dev/null +++ "b/p/v-charts\346\267\273\345\212\240\345\233\276\350\241\250\346\240\207\351\242\230/index.html" @@ -0,0 +1,706 @@ + + + + +v-charts添加图表标题 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ v-charts添加图表标题 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

场景

+

使用 v-charts 做数据可视化,需要给图表添加标题。

+

解决方法

+

v-charts本身并没有提供显示标题的配置,顾需要引入 echartstitle

+

实现

+

引入title

+
+ +
+
1
+
+
import "echarts/lib/component/title";
+
+
+

添加标题配置

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
this.chartTitle = {
+  text: "平台用户与创客数量对比图",
+  textStyle: {
+    fontWeight: 600,
+    color: "white"
+  }
+};
+
+
+

使用

+
+ +
+
1
+
+
<ve-bar :title="chartTitle" />
+
+
+

完整实现

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
+
<template>
+  <ve-bar ... :title="chartTitle" />
+</template>
+
+<script>
+import "echarts/lib/component/title";
+
+export default {
+  data() {
+    this.chartTitle = {
+      text: "平台用户与创客数量对比图",
+      textStyle: {
+        fontWeight: 600,
+        color: "white"
+      }
+    };
+  }
+};
+</script>
+
+
+

echarts配置手册

+ +

参考文章

+ + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250/index.html" "b/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250/index.html" new file mode 100644 index 0000000..8e532e3 --- /dev/null +++ "b/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250/index.html" @@ -0,0 +1,1047 @@ + + + + +Vite+Electron快速构建一个VUE3桌面应用 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Vite+Electron快速构建一个VUE3桌面应用 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

首先,介绍下viteElectron

+
    +
  • Vite是一种新型前端构建工具,能够显著提升前端开发体验。由尤大推出,其发动态表示“再也回不去webpack了…”
  • +
  • Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。
  • +
+

当开始想用vue去开发一个桌面应用时,首先去搜索下,了解到当前如下两种现成方案:

+
    +
  • electron-vue: 该项目集成度较好,封装较为完整,中文搜索下来文章较多也是该方案,可以直接上手去使用。但是,问题在于其内置electron的版本太低,写文章时看到的版本是2.0.4,而最新的electron版本是15.1.2。
  • +
  • Vue CLI Plugin Electron Builder: 该方案是集成到到vue-cli中使用,使用vue add electron-builder后可直接上手,免去了基础配置的步骤。但是其只能在vue-cli下使用,无法配合vite来使用。
  • +
+

因此,若要使用viteelectron,还需要自己来配置。

+

源码:https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1

+

创建一个Vite项目

+

安装 vite

+
+ +
+
1
+
+
yarn create vite
+
+
+

创建项目

+

创建命令如下:

+
+ +
+
1
+
+
yarn create vite <your-vue-app-name> --template vue
+
+
+

此处创建一个项目,名为kuari。

+
+ +
+
1
+
+
yarn create vite kuari --template vue
+
+
+

进入且运行

+

进入项目,在运行前需要先安装下依赖。

+
+ +
+
1
+2
+3
+
+
cd kuari
+yarn install
+yarn dev
+
+
+

在运行命令敲下的一瞬间,几乎是已经在运行了,不愧是vite。此时按照输出,打开地址预览,即可看到初始化页面。

+截屏2021-10-14 下午12.50.48 +

至此一个基础的vite项目创建完成。

+

配置Electron

+

官方文档

+

Electron官网的快速入门文档中,有官方给出的利用html、javascript、css来创建一个electron应用的案例,vite+electron的方案也借鉴其中。

+

安装

+

首先安装electron至vite应用。目前electron的版本为^15.1.2,

+
+ +
+
1
+
+
yarn add --dev electron
+
+
+

配置文件

+

####config.js

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import path from 'path' 										// 新增
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  base: path.resolve(__dirname, './dist/'),	// 新增
+  plugins: [vue()]
+})
+
+
+

####js

+

创建一个新的文件main.js,需要注意的是,该内容中index.html的加载路径跟electron官网给的配置不同。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+
+
// main.js
+
+// 控制应用生命周期和创建原生浏览器窗口的模组
+const { app, BrowserWindow } = require('electron')
+const path = require('path')
+
+function createWindow () {
+  // 创建浏览器窗口
+  const mainWindow = new BrowserWindow({
+    width: 800,
+    height: 600,
+    webPreferences: {
+      preload: path.join(__dirname, 'preload.js')
+    }
+  })
+
+  // 加载 index.html
+  mainWindow.loadFile('dist/index.html') // 此处跟electron官网路径不同,需要注意
+
+  // 打开开发工具
+  // mainWindow.webContents.openDevTools()
+}
+
+// 这段程序将会在 Electron 结束初始化
+// 和创建浏览器窗口的时候调用
+// 部分 API 在 ready 事件触发后才能使用。
+app.whenReady().then(() => {
+  createWindow()
+
+  app.on('activate', function () {
+    // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
+    // 打开的窗口,那么程序会重新创建一个窗口。
+    if (BrowserWindow.getAllWindows().length === 0) createWindow()
+  })
+})
+
+// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
+// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
+app.on('window-all-closed', function () {
+  if (process.platform !== 'darwin') app.quit()
+})
+
+// 在这个文件中,你可以包含应用程序剩余的所有部分的代码,
+// 也可以拆分成几个文件,然后用 require 导入。
+
+
+

####js

+

创建一个新的文件preload.js

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
// preload.js
+
+// 所有Node.js API都可以在预加载过程中使用。
+// 它拥有与Chrome扩展一样的沙盒。
+window.addEventListener('DOMContentLoaded', () => {
+  const replaceText = (selector, text) => {
+    const element = document.getElementById(selector)
+    if (element) element.innerText = text
+  }
+
+  for (const dependency of ['chrome', 'node', 'electron']) {
+    replaceText(`${dependency}-version`, process.versions[dependency])
+  }
+})
+
+
+

####json

+

为了确保能够运行相关electron的命令,需要修改package.json文件。

+

首先需要去设置main属性,electron默认会去在开始时寻找项目根目录下的index.js文件,此处我们使用的是main.js,所以需要去定义下。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
// package.json
+
+{
+  "name": "kuari",
+  "version": "0.0.0",
+  "main": "main.js", 			// 新增
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "serve": "vite preview"
+  },
+  "dependencies": {
+    "vue": "^3.2.16"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^1.9.3",
+    "electron": "^15.1.2",
+    "vite": "^2.6.4"
+  }
+}
+
+
+

最后我们需要新增electron的运行命令。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
// package.json
+
+{
+  "name": "kuari",
+  "version": "0.0.0",
+  "main": "main.js",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "serve": "vite preview",
+    "electron:serve": "electron ." // 新增
+  },
+  "dependencies": {
+    "vue": "^3.2.16"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^1.9.3",
+    "electron": "^15.1.2",
+    "vite": "^2.6.4"
+  }
+}
+
+
+

运行

+

直接在终端输入如下命令:

+
+ +
+
1
+
+
yarn electron:serve
+
+
+

接着我们就可以看到我们桌面应用就出来咯!

+截屏2021-10-14 下午1.32.38 +

最后

+

之前做项目一直用的Vue CLI Plugin Electron Builder,这次有个项目先用electron开发一下,推一波看看,后期看情况swift重新开发一个mac的桌面应用。也刚好尝尝鲜,一直没有机会试试vite。

+

electron这个东东确实很方便,就是打包出来的应用体积太大,真的是硬伤啊。这次目标人群首先是windows用户,所以上electron吧!

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250\344\270\211\346\211\223\345\214\205/index.html" "b/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250\344\270\211\346\211\223\345\214\205/index.html" new file mode 100644 index 0000000..b0b8050 --- /dev/null +++ "b/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250\344\270\211\346\211\223\345\214\205/index.html" @@ -0,0 +1,1115 @@ + + + + +Vite+Electron快速构建一个VUE3桌面应用(三)——打包 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Vite+Electron快速构建一个VUE3桌面应用(三)——打包 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

上一篇文章Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载完成了开发时的动态模块热重载功能,现在是时候来看看怎么完成最后一步——打包了。

+

源码:https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3

+

系列文章:

+ +

思路

+

先说结论,重点还是在于mainWindow.loadURL()

+

打包后还是加载http://localhost:3000是无法运行的,因此,此处需要先用vite打包好,然后使用electron-builder加载vite打包后的文件进行打包。

+

为了代码能够根据不同环境在运行时加载http://localhost:3000,在打包时加载文件,此处需要使用环境变量来切换生产和开发环境。

+

实现

+

环境变量

+

此处使用环境变量NODE_ENV来切换生产和开发环境,生产环境为NODE_ENV=production,开发环境为NODE_ENV=development,若有其它如release等环境可在此基础上拓展。

+

创建electron文件夹

+

在项目根目录下创建文件夹electron,将main.jspreload.js文件移动进来。其结构如下所示:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
.
+├── README.md
+├── electron
+│   ├── main.js
+│   └── preload.js
+...
+
+
+

若还是不太明白可以看看源码中文件结构。

+

编辑electron/main.js

+

该文件主要是需要根据环境变量切换electron加载的内容,修改内容如下:

+
+ +
+
1
+2
+3
+4
+5
+
+
mainWindow.loadURL(
+  NODE_ENV === 'development'
+  ? 'http://localhost:3000'
+  :`file://${path.join(__dirname, '../dist/index.html')}`
+);
+
+
+

修改后的完整内容如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+
+
// electron/main.js
+
+// 控制应用生命周期和创建原生浏览器窗口的模组
+const { app, BrowserWindow } = require('electron')
+const path = require('path')
+
+const NODE_ENV = process.env.NODE_ENV
+
+function createWindow () {
+  // 创建浏览器窗口
+  const mainWindow = new BrowserWindow({
+    width: 800,
+    height: 600,
+    webPreferences: {
+      preload: path.join(__dirname, 'preload.js')
+    }
+  })
+
+  // 加载 index.html
+  // mainWindow.loadFile('dist/index.html') 将该行改为下面这一行,加载url
+  mainWindow.loadURL(
+    NODE_ENV === 'development'
+      ? 'http://localhost:3000'
+      :`file://${path.join(__dirname, '../dist/index.html')}`
+  );
+
+  // 打开开发工具
+  if (NODE_ENV === "development") {
+    mainWindow.webContents.openDevTools()
+  }
+
+}
+
+// 这段程序将会在 Electron 结束初始化
+// 和创建浏览器窗口的时候调用
+// 部分 API 在 ready 事件触发后才能使用。
+app.whenReady().then(() => {
+  createWindow()
+
+  app.on('activate', function () {
+    // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
+    // 打开的窗口,那么程序会重新创建一个窗口。
+    if (BrowserWindow.getAllWindows().length === 0) createWindow()
+  })
+})
+
+// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
+// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
+app.on('window-all-closed', function () {
+  if (process.platform !== 'darwin') app.quit()
+})
+
+// 在这个文件中,你可以包含应用程序剩余的所有部分的代码,
+// 也可以拆分成几个文件,然后用 require 导入。
+
+
+

编辑package.json

+

首先修改main 属性,将main: main.js改为main: electron/main.js

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
{
+  "name": "kuari",
+  "version": "0.0.0",
+  "main": "electron/main.js",
+ 	... 
+}
+
+
+

接着,编辑build属性:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
"build": {
+  "appId": "com.your-website.your-app",
+  "productName": "ElectronApp",
+  "copyright": "Copyright © 2021 <your-name>",
+  "mac": {
+    "category": "public.app-category.utilities"
+  },
+  "nsis": {
+    "oneClick": false,
+    "allowToChangeInstallationDirectory": true
+  },
+  "files": [
+    "dist/**/*",
+    "electron/**/*"
+  ],
+  "directories": {
+    "buildResources": "assets",
+    "output": "dist_electron"
+  }
+}
+
+
+

然后,更新scripts属性。

+

此处需要先安装两个库:

+
    +
  • cross-env: 该库让开发者只需要注重环境变量的设置,而无需担心平台设置
  • +
  • electron-builder: electron打包库
  • +
+
+ +
+
1
+
+
yarn add -D cross-env electron-builder
+
+
+

更新后的scripts如下:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
{
+  "dev": "vite",
+  "build": "vite build",
+  "serve": "vite preview",
+  "electron": "wait-on tcp:3000 && cross-env NODE_ENV=development electron .", // 此处需要设置环境变量以保证开发时加载url
+  "electron:serve": "concurrently -k \"yarn dev\" \"yarn electron\"",
+  "electron:build": "vite build && electron-builder" // 新增打包命令
+}
+
+
+

最后,更新后的package.json完整内容如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+
+
{
+  "name": "kuari",
+  "version": "0.0.0",
+  "main": "electron/main.js",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "serve": "vite preview",
+    "electron": "wait-on tcp:3000 && cross-env NODE_ENV=development electron .",
+    "electron:serve": "concurrently -k \"yarn dev\" \"yarn electron\"",
+    "electron:build": "vite build && electron-builder"
+  },
+  "dependencies": {
+    "vue": "^3.2.16"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^1.9.3",
+    "concurrently": "^6.3.0",
+    "cross-env": "^7.0.3",
+    "electron": "^15.1.2",
+    "electron-builder": "^22.13.1",
+    "vite": "^2.6.4",
+    "wait-on": "^6.0.0"
+  },
+  "build": {
+    "appId": "com.my-website.my-app",
+    "productName": "MyApp",
+    "copyright": "Copyright © 2021 kuari",
+    "mac": {
+      "category": "public.app-category.utilities"
+    },
+    "nsis": {
+      "oneClick": false,
+      "allowToChangeInstallationDirectory": true
+    },
+    "files": [
+      "dist/**/*",
+      "electron/**/*"
+    ],
+    "directories": {
+      "buildResources": "assets",
+      "output": "dist_electron"
+    }
+  }
+}
+
+
+

打包

+

直接执行打包命令即可开始打包。

+
+ +
+
1
+
+
yarn electron:build
+
+
+

打包完成之后,会多出两个文件夹distdist_electron,其文件结构如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
.
+├── README.md
+├── dist
+│   ├── assets
+│   ├── favicon.ico
+│   └── index.html
+├── dist_electron
+│   ├── MyApp-0.0.0-mac.zip
+│   ├── MyApp-0.0.0-mac.zip.blockmap
+│   ├── MyApp-0.0.0.dmg
+│   ├── MyApp-0.0.0.dmg.blockmap
+│   ├── builder-debug.yml
+│   ├── builder-effective-config.yaml
+│   └── mac
+...
+
+
+

至此,便完成了打包。

+

后面再来写写关于electron的优化,减少electron打包后应用的体积。(这玩意儿确实打包下来有点大呢/狗头)

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250\344\272\214\345\212\250\346\200\201\346\250\241\345\235\227\347\203\255\351\207\215\350\275\275/index.html" "b/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250\344\272\214\345\212\250\346\200\201\346\250\241\345\235\227\347\203\255\351\207\215\350\275\275/index.html" new file mode 100644 index 0000000..431c0aa --- /dev/null +++ "b/p/vite-electron\345\277\253\351\200\237\346\236\204\345\273\272\344\270\200\344\270\252vue3\346\241\214\351\235\242\345\272\224\347\224\250\344\272\214\345\212\250\346\200\201\346\250\241\345\235\227\347\203\255\351\207\215\350\275\275/index.html" @@ -0,0 +1,924 @@ + + + + +Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

在上一篇文章Vite+Electron快速构建一个VUE3桌面应用中,我们了解了如何使用ViteElectron来快速构建一个Vue3桌面应用。但是,之前构建的应用仅仅是一个简单的版本。在开发过程中,为了更好的开发体验,在开发electron的时候,肯定也希望能有动态模块热重载(HMR),更别说vite那迅雷不及掩耳盗铃儿响叮当之势的加载速度。

+

因此,接着上一篇文章所完成的项目代码,我们来完成ViteElectron开发时的动态模块热重载功能。

+

源码:https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2

+

系列文章:

+ +

思路

+

先说结论,可利用electron中的mainWindow.loadURL(<your-url>)来实现。

+

对于动态模块热重载功能来说,无论是webpack还是vite,其都是将构建内容存入内存,因此我们无法使用mainWindow.loadFile('dist/index.html')这样加载文件的方式。

+

但是,单纯地改变该配置也是不行的,需要使用vite将开发服务器运行起来,可以正常运行动态模块热重载,而electron直接加载其开发服务器可访问的url,即http://localhost:3000

+

实现步骤

+

编辑main.js

+

mainWindow.loadFile('dist/index.html')更新为mainWindow.loadURL("http://localhost:3000"),更新后的文件如下所示:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+
+
// main.js
+
+// 控制应用生命周期和创建原生浏览器窗口的模组
+const { app, BrowserWindow } = require('electron')
+const path = require('path')
+
+function createWindow () {
+  // 创建浏览器窗口
+  const mainWindow = new BrowserWindow({
+    width: 800,
+    height: 600,
+    webPreferences: {
+      preload: path.join(__dirname, 'preload.js')
+    }
+  })
+
+  // 加载 index.html
+  // mainWindow.loadFile('dist/index.html') 将该行改为下面这一行,加载url
+  mainWindow.loadURL("http://localhost:3000")
+
+  // 打开开发工具
+  // mainWindow.webContents.openDevTools()
+}
+
+// 这段程序将会在 Electron 结束初始化
+// 和创建浏览器窗口的时候调用
+// 部分 API 在 ready 事件触发后才能使用。
+app.whenReady().then(() => {
+  createWindow()
+
+  app.on('activate', function () {
+    // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
+    // 打开的窗口,那么程序会重新创建一个窗口。
+    if (BrowserWindow.getAllWindows().length === 0) createWindow()
+  })
+})
+
+// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
+// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
+app.on('window-all-closed', function () {
+  if (process.platform !== 'darwin') app.quit()
+})
+
+// 在这个文件中,你可以包含应用程序剩余的所有部分的代码,
+// 也可以拆分成几个文件,然后用 require 导入。
+
+
+

编辑vite.config.js

+

修改文件vite.config.jsbase,修改后的文件如下所示:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
// vite.config.js
+
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  base: "./",	// 新增
+  plugins: [vue()]
+})
+
+
+

同时开启vite和electron服务

+

为了使vite和electron正常运行,需要先运行vite,使得其开发服务器的url可以正常访问,然后再开启electron去加载url。

+

此处需要安装两个库:

+
    +
  • concurrently:阻塞运行多个命令,-k参数用来清除其它已经存在或者挂掉的进程
  • +
  • wait-on:等待资源,此处用来等待url可访问
  • +
+

首先来安装。

+
+ +
+
1
+
+
yarn add -D concurrently wait-on
+
+
+

接着更新文件package.jsonscripts新增两条命令:

+
+ +
+
1
+2
+3
+4
+
+
  "scripts": {
+    "electron": "wait-on tcp:3000 && electron .",
+    "electron:serve": "concurrently -k \"yarn dev\" \"yarn electron\""
+  },
+
+
+

更新后完整内容如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
+
{
+  "name": "kuari",
+  "version": "0.0.0",
+  "main": "main.js",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "serve": "vite preview",
+    "electron": "wait-on tcp:3000 && electron .",
+    "electron:serve": "concurrently -k \"yarn dev\" \"yarn electron\""
+  },
+  "dependencies": {
+    "vue": "^3.2.16"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^1.9.3",
+    "concurrently": "^6.3.0",
+    "cross-env": "^7.0.3",
+    "electron": "^15.1.2",
+    "vite": "^2.6.4",
+    "wait-on": "^6.0.0"
+  }
+}
+
+
+

运行

+

现已添加两条命令:

+
    +
  • yarn electron为等待tcp协议3000端口可访问,然后执行electron
  • +
  • yarn electron:serve为阻塞执行开发服务器运行和yarn electron命令
  • +
+

运行项目只要执行命令yarn electron:serve即可,当修改项目文件时,桌面应用也将自动更新。

+

参考文件

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue-flask\345\211\215\345\220\216\347\253\257\344\277\241\346\201\257\344\274\240\350\276\223\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206/index.html" "b/p/vue-flask\345\211\215\345\220\216\347\253\257\344\277\241\346\201\257\344\274\240\350\276\223\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206/index.html" new file mode 100644 index 0000000..67bf0e0 --- /dev/null +++ "b/p/vue-flask\345\211\215\345\220\216\347\253\257\344\277\241\346\201\257\344\274\240\350\276\223\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206/index.html" @@ -0,0 +1,920 @@ + + + + +vue+flask前后端信息传输非对称加密 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ vue+flask前后端信息传输非对称加密 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

介绍

+

为了前后端传输数据的安全性,需要对数据进行加密。因此选定使用非对称加密,此处为RSA

+

在传输数据前,后端生成公钥和私钥,将公钥给前端,前端加密之后,将密文传给后端,后端使用私钥解密即可得到原始的数据。

+

环境

+
    +
  • 前端使用jsencrypt加密
  • +
  • 后端使用Crypto生成密钥和解密
  • +
+

后端生成密钥

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
from Crypto.PublicKey import RSA
+
+def generate_key():
+    """
+    生成公钥和私钥
+    
+    :return: 返回私钥和公钥
+    """
+    rsa = RSA.generate(1024)
+    private_key = rsa.exportKey()
+    publick_key = rsa.publickey().exportKey()
+    return private_key.decode(), publick_key.decode()
+
+
+

前端用公钥加密

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
import { JSEncrypt } from "jsencrypt";
+
+function rsa_en(pubkey, target_str) {
+  /** 
+  分段加密信息
+  
+  :params target_str: 需要加密的信息,此处为很长的信息
+  :pubkey: 公钥
+  :return: 存储密文的数组
+  **/
+  let encrypt = new JSEncrypt();
+	encrypt.setPublicKey(pubkey);
+	let result = encrypt.encrypt(JSON.stringify(target_str));
+}
+
+
+

后端使用私钥解密

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
+
from Crypto.PublicKey import RSA
+from Crypto.Cipher import PKCS1_v1_5
+from Crypto.Hash import SHA
+from Crypto import Random
+from base64 import b64decode
+
+def rsa_decrypt(private_key, message):
+    """
+    rsa解密函数
+
+    :prams private_key: 私钥
+    :params message: 加密后的密文
+    :return: 解密后原始信息
+    """
+    dsize = SHA.digest_size
+    sentinel = Random.new().read(1024 + dsize)
+    private_key = RSA.import_key(private_key)
+    cipher_rsa = PKCS1_v1_5.new(private_key)
+    return cipher_rsa.decrypt(b64decode(message), sentinel)
+
+
+
    +
  • 这里使用base64先解密一遍是必要的,否则报错ValueError: Ciphertext with incorrect length.
  • +
+

完整代码

+

前端

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
import { JSEncrypt } from "jsencrypt";
+
+function rsa_en(pubkey, target_str) {
+  /** 
+  分段加密信息
+  
+  :params target_str: 需要加密的信息,此处为很长的信息
+  :pubkey: 公钥
+  :return: 存储密文的数组
+  **/
+  let encrypt = new JSEncrypt();
+	encrypt.setPublicKey(pubkey);
+	let result = encrypt.encrypt(JSON.stringify(target_str));
+}
+
+
+

后端

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+
+
from Crypto.PublicKey import RSA
+from Crypto.Cipher import PKCS1_v1_5
+from Crypto.Hash import SHA
+from Crypto import Random
+from base64 import b64decode
+
+
+def generate_key():
+    """
+    生成公钥和私钥
+
+    :return: 返回私钥和公钥
+    """
+    rsa = RSA.generate(1024)
+    private_key = rsa.exportKey()
+    publick_key = rsa.publickey().exportKey()
+    return private_key.decode(), publick_key.decode()
+
+
+def rsa_encrypt(public_key, message):
+    """
+    rsa加密函数
+
+    :params publick_key: 公钥
+    :params message: 需要加密的信息
+    :return: 加密后的密文
+    """
+    public_key = RSA.import_key(public_key)
+    cipher_rsa = PKCS1_v1_5.new(public_key)
+    return cipher_rsa.encrypt(str.encode(message))
+
+
+def rsa_decrypt(private_key, message):
+    """
+    rsa解密函数
+
+    :prams private_key: 私钥
+    :params message: 加密后的密文
+    :return: 解密后原始信息
+    """
+    dsize = SHA.digest_size
+    sentinel = Random.new().read(1024 + dsize)
+    private_key = RSA.import_key(private_key)
+    cipher_rsa = PKCS1_v1_5.new(private_key)
+    return cipher_rsa.decrypt(b64decode(message), sentinel)
+
+
+

分段加密

+

RSA加密信息最长为128位,过长则会报错,因此,对于过长的信息需要分段加密,后端也要分段解密后拼装。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
import { JSEncrypt } from "jsencrypt";
+
+
+function en_str(target_str, pubkey) {
+  /** 
+  分段加密信息
+  
+  :params target_str: 需要加密的信息,此处为很长的信息
+  :pubkey: 公钥
+  :return: 存储密文的数组
+  **/
+  let encrypt = new JSEncrypt();
+	encrypt.setPublicKey(pubkey);
+  let en_array = [];
+	let n = 100; // 每段信息的长度
+	for (let i = 0, l = target_str.length; i < l / n; i++) {
+		let message = target_str.slice(n * i, n * (i + 1));
+		en_array.push(encrypt.encrypt(message));
+	}
+  return en_array;
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue2\345\256\236\347\216\260\345\256\236\346\227\266\347\224\237\346\210\220\344\272\214\347\273\264\347\240\201\345\222\214\345\260\206\347\275\221\351\241\265\345\220\210\346\210\220\345\233\276\347\211\207\345\271\266\345\234\250\345\276\256\344\277\241\345\206\205\347\275\256\346\265\217\350\247\210\345\231\250\351\225\277\346\214\211\344\277\235\345\255\230/index.html" "b/p/vue2\345\256\236\347\216\260\345\256\236\346\227\266\347\224\237\346\210\220\344\272\214\347\273\264\347\240\201\345\222\214\345\260\206\347\275\221\351\241\265\345\220\210\346\210\220\345\233\276\347\211\207\345\271\266\345\234\250\345\276\256\344\277\241\345\206\205\347\275\256\346\265\217\350\247\210\345\231\250\351\225\277\346\214\211\344\277\235\345\255\230/index.html" new file mode 100644 index 0000000..ec68cf3 --- /dev/null +++ "b/p/vue2\345\256\236\347\216\260\345\256\236\346\227\266\347\224\237\346\210\220\344\272\214\347\273\264\347\240\201\345\222\214\345\260\206\347\275\221\351\241\265\345\220\210\346\210\220\345\233\276\347\211\207\345\271\266\345\234\250\345\276\256\344\277\241\345\206\205\347\275\256\346\265\217\350\247\210\345\231\250\351\225\277\346\214\211\344\277\235\345\255\230/index.html" @@ -0,0 +1,918 @@ + + + + +vue2实现实时生成二维码和将网页合成图片并在微信内置浏览器长按保存 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ vue2实现实时生成二维码和将网页合成图片并在微信内置浏览器长按保存 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

url转为二维码

+

需要的库

+

qrcodejs2

+

安装

+
+ +
+
1
+
+
npm install qrcodejs2 --save
+
+
+

引入

+
+ +
+
1
+
+
import QRCode from "qrcodejs2"
+
+
+

实现

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+
+
<template>
+	<div>
+    <div id="qrcode"></div>
+  </div>
+</template>
+
+<script>
+import QRCode from "qrcodejs2";
+ 
+export default {
+  methods: {
+    GenerateQRcode() {
+      new QRCode("qrcode", { // 此处的qrcode为上面div的id
+        text: 目标url,
+        width: 200,
+        height: 200,
+        colorDark: "#000000",
+        colorLight: "#ffffff",
+        correctLevel: QRCode.CorrectLevel.H
+      });
+    }
+  },
+  mounted() {
+    this.GenerateQRcode();
+  }
+}
+</script>
+
+
+

网页保存为图片

+

需要的库

+

html2canvas

+

安装

+
+ +
+
1
+
+
npm install html2canvas --save
+
+
+

引入

+
+ +
+
1
+
+
import html2canvas from "html2canvas"
+
+
+

实现

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
+
<template>
+	<div>
+  	<div id="container"></div>
+	</div>
+</template>
+
+<script>
+import html2canvas from "html2canvas";
+import QRCode from "qrcodejs2";
+
+export default {
+  methods: {
+    outputImg() {
+    	const targetDom = document.getElementById("container");
+    	html2canvas(targetDom).then(canvas => {
+      	console.log(canvas);
+      	console.log(canvas.toDataURL());
+    	});
+  	}
+  },
+  mounted() {
+    this.outputImg();
+  }
+}
+</script>
+
+
+

整合

+

关于小程序内置浏览器的图片下载,需要一个用来生成图片的块,还需要一个img,先将其隐藏。实现步骤就是首先生成二维码,然后再将html生成图片,最后在html2canvas回调中替换imgsrc,并将生成图片的块隐藏,将img显示。

+

当然关于这个实现方式,我看到的技术分享文章中,还有两种不同的解决方式:

+
    +
  • 不需要html来写生成图片的块,而是使用js直接创建;
  • +
  • 不需要替换隐藏,将生成的图片覆盖到html生成图片的块之前;
  • +
+

这里我只记录一下我使用的,后期会再去研究这两种实现方式。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+
+
<template>
+	<div>
+    <!--合成的图片默认隐藏合成之后显示-->
+    <div v-show="imgUrl.length">
+      <img :src="imgUrl" alt="生成的图片" class="image" />
+  	</div>
+    <!--合成图片需要的html块默认显示合成之后隐藏-->
+  	<div id="container" v-show="!imgUrl.length">
+    	<div id="qrcode"></div>
+      <p>长按识别二维码</p>
+  	</div>
+  </div>
+</template>
+
+<script>
+import html2canvas from "html2canvas";
+
+export default {
+  data() {
+    return {
+      imgUrl: ""
+    }
+  },
+  methods: {
+    outputImg() {
+    	const targetDom = document.getElementById("container");
+    	html2canvas(targetDom).then(canvas => {
+        // 将图片src替换为canvas生成之后转换的url
+      	this.imgUrl = canvas.toDataURL();
+    	});
+  	},
+    GenerateQRcode() {
+      new QRCode("qrcode", {
+        text: 目标url,
+        width: 200,
+        height: 200,
+        colorDark: "#000000",
+        colorLight: "#ffffff",
+        correctLevel: QRCode.CorrectLevel.H
+      });
+    }
+  },
+  mounted() {
+    new Promise(resolve => {
+      // 先生成二维码
+      this.GenerateQRcode();
+      resove();
+    })
+    .then(() => {
+      // 再合成图片
+      this.outputImg();
+    })
+  }
+}
+</script>
+
+<style scoped>
+  // 生成之后的图片有点放肆,可以设置宽度来适应手机屏幕
+ .image {
+    width: 100%;
+ }
+</style>
+
+
+

由此即可实现需要的功能了。

+

关于后续的优化,需要解决的图片清晰度问题、跨域图片问题等,可以参考这篇文章,这位大佬写得很详细。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue3-script-setup\345\223\215\345\272\224\345\274\217\345\210\235\344\275\223\351\252\214/index.html" "b/p/vue3-script-setup\345\223\215\345\272\224\345\274\217\345\210\235\344\275\223\351\252\214/index.html" new file mode 100644 index 0000000..fa38ee2 --- /dev/null +++ "b/p/vue3-script-setup\345\223\215\345\272\224\345\274\217\345\210\235\344\275\223\351\252\214/index.html" @@ -0,0 +1,843 @@ + + + + +vue3 script setup响应式初体验 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ vue3 script setup响应式初体验 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

最近空下来,正好找个项目尝鲜,把vue3+ts+setup哐哐全堆上,试试最新的前端技术。

+

从最先体会到的变化,就是关于响应式APIs了。遇到不好问题,怪我没有理解文档/狗头。比如说:

+
    +
  • 明明这个数据改了,怎么没渲染出来?
  • +
  • 同样是Arrary,怎么套了个reactive就类型不一样了?
  • +
+

所以这里基于遇到的几个问题,来写个笔记。

+

简单对比

+ +

官方案例如下:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
<script setup>
+import { ref } from 'vue'
+
+const count = ref(0)
+</script>
+
+<template>
+  <button @click="count++">{{ count }}</button>
+</template>
+
+
+

我们可以先看看,如果是vue2,怎么做呢?

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
<script>
+export default {
+  data() {
+    return {
+      count: 0
+    }
+  }
+}
+</script>
+
+<template>
+	<button @click="count++">{{ count }}</button>
+</template>
+
+
+

可以很明显地看到此处的count跟上面的官方文档不同,使用了ref方法。这就是setup中的响应式APIs,需要预先声明响应式变量。

+

如果,不声明呢?那就是直接写成如下:

+
+ +
+
1
+
+
const count = 0
+
+
+

运行一下,首先你会发现,没有任何报错,代码正常运行。但是当你在浏览器上查看,开始改变count的值时,就会发现,怎么页面没有变化?

+

所以,需要手动命名响应,才会在值变化时触发视图渲染。

+

ref

+

现在来详细讲讲ref,该方法常用于单个变量,比如:

+
+ +
+
1
+2
+
+
ref(0)
+ref("hello")
+
+
+

这里需要说明一个问题,那就是ref("hello") !== "hello"。这就是我说的,为什么同样的值,类型就不同了。那是因为ref返回的是一个Proxy,而非原来的值。在视图中可直接使用,但是在js/ts中操作,需要使用.value来操作,如下所示:

+
+ +
+
1
+2
+3
+4
+5
+
+
let name = ref("kuari")
+
+function changeName() {
+  name.value = "tom"
+}
+
+
+

reactive

+

reactive不同于ref的点在于,其是“深层”的——它影响所有嵌套 property。也就是说,其可用在对象或者数组上。

+
+ +
+
1
+2
+3
+4
+
+
let form = reactive({
+  name: "kuari",
+  desc: "developer"
+})
+
+
+

其返回类型也是Proxy,不同点在于,可以直接修改某一个元素的,如下所示:

+
+ +
+
1
+
+
form.name = "tom"
+
+
+

但是如果你想整个替换就会报错了。

+
+ +
+
1
+
+
form = {...} // 报错,类型不同
+
+
+

当使用的是数组时,如果想整个替换,可以将其写成对象,代码如下所示:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
+
let selected = reactive({
+  arr: [
+    {
+      label: "vue",
+      value: 0
+    },
+    {
+      label: "typescript",
+      value: 1
+		}
+  ]
+})
+
+// 使用
+console.log(selected.arr)
+
+// 整个替换
+selected.arr = [...]
+
+
+

关于reactiveref一起使用,reactive 将解包所有深层的ref,同时维持 ref 的响应性。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
const count = ref(1)
+const obj = reactive({ count })
+
+// ref 会被解包
+console.log(obj.count === count.value) // true
+
+// 它会更新 `obj.count`
+count.value++
+console.log(count.value) // 2
+console.log(obj.count) // 2
+
+// 它也会更新 `count` ref
+obj.count++
+console.log(obj.count) // 3
+console.log(count.value) // 3
+
+
+

总结

+

setup总体来说,用起来真的会更加简洁,而响应式虽然好像比之前麻烦些了,但是一定层面上让开发对对于程序有了更深入的操控。墙裂推荐一波!后面再来详细讲讲对于新特性的体验。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue3-ts-electron\344\270\215\346\224\257\346\214\201require-is-not-defined\346\212\245\351\224\231\350\247\243\345\206\263/index.html" "b/p/vue3-ts-electron\344\270\215\346\224\257\346\214\201require-is-not-defined\346\212\245\351\224\231\350\247\243\345\206\263/index.html" new file mode 100644 index 0000000..303b686 --- /dev/null +++ "b/p/vue3-ts-electron\344\270\215\346\224\257\346\214\201require-is-not-defined\346\212\245\351\224\231\350\247\243\345\206\263/index.html" @@ -0,0 +1,706 @@ + + + + +vue3+ts+electron不支持require is not defined报错解决 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ vue3+ts+electron不支持require is not defined报错解决 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

报错

+

在使用vue3+typescript+electron开发时,遇到一个报错为:

+
+ +
+
1
+
+
Uncaught ReferenceError: require is not defined
+
+
+

点进去是module.exports = require("events"),并不是自己的代码中的require,因此无法改变写法只能让项目去支持它。

+

解决

+

在electron的配置文件中,新增或者修改如下配置:

+
+ +
+
1
+2
+3
+4
+5
+
+
webPreferences: {
+  // ...
+  contextIsolation: false,
+  nodeIntegration: true
+}
+
+
+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue3-ts-\345\276\256\345\211\215\347\253\257\345\256\236\350\267\265/index.html" "b/p/vue3-ts-\345\276\256\345\211\215\347\253\257\345\256\236\350\267\265/index.html" new file mode 100644 index 0000000..06c3837 --- /dev/null +++ "b/p/vue3-ts-\345\276\256\345\211\215\347\253\257\345\256\236\350\267\265/index.html" @@ -0,0 +1,1268 @@ + + + + +VUE3+TS+微前端实践 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ VUE3+TS+微前端实践 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

基于架构的调整,前端开始转为微前端。经过调研,决定使用qiankun微服务框架来使用,本文将介绍VUE3+TS+qiankun的实践经过。微服务架构的优势之一在于可以结合不同技术栈的节点,基于技术栈的考虑,此处用的都是vue3。

+

源码:https://github.com/Kuari/Blog/tree/master/Examples/microFrontend

+

环境

+
    +
  • vue 3.0.0
  • +
  • TypeScript 4.1.5
  • +
  • vue router 4.0.0
  • +
  • @vue/cli 4.5.15
  • +
  • qiankun 2.6.3
  • +
+

实践

+

架构

+

qiankun_example

+

如上图所示,微服务架构将会由多个节点构成,首先由一个主节点site_base连接所有子节点,子节点可以不断拓展。

+

主节点

+

主节点源码可见于https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base

+

创建主节点,选择vue3+ts

+
+ +
+
1
+2
+
+
vue create site_base
+cd site_base
+
+
+

安装qiankun

+
+ +
+
1
+
+
yarn add qiankun
+
+
+

src/App.vue中添加路由和渲染节点

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
<template>
+  <div id="nav">
+    <router-link to="/">Home</router-link> |
+    <router-link to="/about">About</router-link> |
+    <!-- 新增site1路由 -->
+    <router-link to="/site1">Site1</router-link> |
+    <!-- 新增site2路由 -->
+    <router-link to="/site2">Site2</router-link>
+  </div>
+  <router-view/>
+  <!-- 新增site1渲染节点 -->
+  <div id="site1" />
+  <!-- 新增site2渲染节点 -->
+  <div id="site2" />
+</template>
+
+
+

src/main.ts中引入子节点配置

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
+
import { createApp } from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import { registerMicroApps, start } from 'qiankun'
+
+const apps: any[] = [
+  {
+    name: 'site1', // 应用的名字
+    entry: 'http://localhost:9001/', // 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch)
+    container: '#site1', // 要渲染到的节点id,对应上一步中src/App.vue中的渲染节点
+    activeRule: '/site1' // 访问子节点路由
+  },
+  {
+    name: 'site2',
+    entry: 'http://localhost:9002/',
+    container: '#site2',
+    activeRule: '/site2'
+  }
+]
+
+registerMicroApps(apps) // 注册应用
+start() // 开启应用
+
+createApp(App).use(store).use(router).mount('#app')
+
+
+

子节点

+

子节点源码可见于https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1

+

此处以site1为例,site2同理。

+

创建子节点,选择vue3+ts

+
+ +
+
1
+2
+
+
vue create site_1
+cd site_1
+
+
+

编辑src/App.vue

+
+ +
+
1
+2
+3
+
+
<template>
+  <router-view />
+</template>
+
+
+

编辑src/views/Home.vue,修改其内容,写一点标识性的文本

+
+ +
+
1
+2
+3
+
+
<template>
+  <div>Hello, Site1!</div>
+</template>
+
+
+

创建文件src/pulic-path.ts,第一行的注视一定要加,避免eslint对于变量的报错

+
+ +
+
1
+2
+3
+4
+
+
/* eslint-disable camelcase */
+if ((window as any).__POWERED_BY_QIANKUN__) {
+  __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__
+}
+
+
+

编辑src/router/index.ts,此处直接返回routes,而不是router,并且修改

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+
+
import { RouteRecordRaw } from 'vue-router'
+
+const routes: Array<RouteRecordRaw> = [
+  {
+    path: '/',
+    name: 'Home',
+    component: () => import('../views/Home.vue')
+  },
+  {
+    path: '/about',
+    name: 'About',
+    component: () => import('../views/About.vue')
+  }
+]
+
+// 直接返回routes,由其它地方处理创建路由
+export default routes
+
+
+

编辑src/main.ts

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+
+
import './public-path'
+import { createApp } from 'vue'
+import { createRouter, createWebHistory } from 'vue-router'
+import App from './App.vue'
+import routes from './router'
+import store from './store'
+
+let router = null
+let instance: any = null
+let history: any = null
+
+function render (props = {}) {
+  const { container } = props
+  // 当为微服务主节点情况下访问,会设置二级路径,而直接访问时没有二级路径,此处需要根据实际情况修改
+  history = createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/site1' : '/')
+  router = createRouter({
+    history,
+    routes
+  })
+
+  instance = createApp(App)
+  instance.use(router)
+  instance.use(store)
+  instance.mount(container ? container.querySelector('#app') : '#app')
+}
+
+if (!window.__POWERED_BY_QIANKUN__) {
+  render()
+}
+
+export const bootstrap = async (): Promise<void> => {
+  console.log('%c ', 'color: green ', 'vue3.0 app bootstraped')
+}
+
+const storeTest = (props: any): void => {
+  props.onGlobalStateChange &&
+  props.onGlobalStateChange(
+    (value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev),
+    true
+  )
+  props.setGlobalState &&
+  props.setGlobalState({
+    ignore: props.name,
+    user: {
+      name: props.name
+    }
+  })
+}
+
+export const mount = async (props: any): Promise<void> => {
+  storeTest(props)
+  render(props)
+  instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange
+  instance.config.globalProperties.$setGlobalState = props.setGlobalState
+}
+
+export const unmount = async (): Promise<void> => {
+  instance.unmount()
+  instance._container.innerHTML = ''
+  instance = null
+  router = null
+  history.destroy()
+}
+
+
+

创建文件vue.config.js

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+
+
const path = require('path')
+const { name } = require('./package')
+
+function resolve (dir) {
+  return path.join(__dirname, dir)
+}
+
+const port = 9001
+
+module.exports = {
+  outputDir: 'dist',
+  assetsDir: 'static',
+  filenameHashing: true,
+  devServer: {
+    hot: true,
+    disableHostCheck: true,
+    port,
+    overlay: {
+      warnings: false,
+      errors: true
+    },
+    headers: {
+      'Access-Control-Allow-Origin': '*'
+    }
+  },
+  // 自定义webpack配置
+  configureWebpack: {
+    resolve: {
+      alias: {
+        '@': resolve('src')
+      }
+    },
+    output: {
+      // 把子应用打包成 umd 库格式
+      library: `${name}-[name]`,
+      libraryTarget: 'umd',
+      jsonpFunction: `webpackJsonp_${name}`
+    }
+  }
+}
+
+
+

验证

+

主节点和子节点分别独立运行,但是子节点的地址需要跟主节点配置中子节点对应的地址相同。

+

在主节点上点击子节点的路由,即可在主节点上访问子节点的页面了!

+qiangkun_example_result +

主节点优化

+

主节点除了如上配置,可以进行两项优化:

+
    +
  • 模块化子节点配置
  • +
  • 添加过渡状态,当加载子节点时窗口顶部出现加载进度条
  • +
+

优化后主节点源码可见于https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize

+

模块化子节点配置

+

创建文件夹src/childNodes,然后创建文件src/childNodes/apps.ts

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
const apps: any[] = [
+  {
+    name: 'site1', // 应用的名字
+    entry: 'http://localhost:9001/', // 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch)
+    container: '#site1', // 要渲染到的节点id
+    activeRule: '/site1' // 访问子节点路由
+  }
+]
+
+export default apps
+
+
+

创建文件src/childNodes/index.ts

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
import { registerMicroApps, start } from 'qiankun'
+import apps from './apps'
+
+registerMicroApps(apps)
+
+export default start
+
+
+

编辑src/main.ts

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
import { createApp } from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import start from './childNodes'
+
+start() // 开启应用
+
+createApp(App).use(store).use(router).mount('#app')
+
+
+

过渡效果

+

此处的过渡效果采用NProgress库,先来安装一波

+
+ +
+
1
+
+
yarn add nprogress
+
+
+

编辑src/childNodes/index.ts

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
import { addGlobalUncaughtErrorHandler, registerMicroApps, start } from 'qiankun'
+import apps from './apps'
+
+import NProgress from 'nprogress'
+import 'nprogress/nprogress.css'
+
+registerMicroApps(apps, {
+  // qiankun 生命周期钩子 - 子节点加载前
+  beforeLoad: (app: any) => {
+    NProgress.start() // 开始进度条
+    return Promise.resolve()
+  },
+  // qiankun 生命周期钩子 - 子节点挂载后
+  afterMount: (app: any) => {
+    NProgress.done() // 进度条结束
+    return Promise.resolve()
+  }
+})
+
+export default start
+
+
+

结语

+

qiankun框架确实挺不错的,配置也并不是复杂,但是唯一想吐槽的一点是对于ts的支持感觉不太好/狗头,或许是我写得不够好吧,后面会持续优化使用。

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue\345\256\236\347\216\260\347\272\257\345\211\215\347\253\257\345\257\274\345\205\245\344\270\216\350\247\243\346\236\220excel\350\241\250\346\240\274\346\226\207\344\273\266/index.html" "b/p/vue\345\256\236\347\216\260\347\272\257\345\211\215\347\253\257\345\257\274\345\205\245\344\270\216\350\247\243\346\236\220excel\350\241\250\346\240\274\346\226\207\344\273\266/index.html" new file mode 100644 index 0000000..8d353b0 --- /dev/null +++ "b/p/vue\345\256\236\347\216\260\347\272\257\345\211\215\347\253\257\345\257\274\345\205\245\344\270\216\350\247\243\346\236\220excel\350\241\250\346\240\274\346\226\207\344\273\266/index.html" @@ -0,0 +1,956 @@ + + + + +vue实现纯前端导入与解析excel表格文件 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ vue实现纯前端导入与解析excel表格文件 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

场景

+

前端导入excel表格,直接前端解析文件,将数据传给后端。

+

需要的库

+

安装

+
+ +
+
1
+
+
npm install xlsx
+
+
+

使用

+
+ +
+
1
+
+
import XLSX from "xlsx";
+
+
+

代码实现

+

html部分

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
    <div class="container">
+      {{ upload_file || "导入" }}
+      <input
+        type="file"
+        accept=".xls,.xlsx"
+        class="upload_file"
+        @change="readExcel($event)"
+      />
+    </div>
+  </div>
+
+
+

JS部分

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+
+
readExcel(e) {
+      // 读取表格文件
+      let that = this;
+      const files = e.target.files;
+      if (files.length <= 0) {
+        return false;
+      } else if (!/\.(xls|xlsx)$/.test(files[0].name.toLowerCase())) {
+        this.$message({
+          message: "上传格式不正确,请上传xls或者xlsx格式",
+          type: "warning"
+        });
+        return false;
+      } else {
+        // 更新获取文件名
+        that.upload_file = files[0].name;
+      }
+
+      const fileReader = new FileReader();
+      fileReader.onload = ev => {
+        try {
+          const data = ev.target.result;
+          const workbook = XLSX.read(data, {
+            type: "binary"
+          });
+          const wsname = workbook.SheetNames[0]; //取第一张表
+          const ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]); //生成json表格内容
+					console.log(ws);
+        } catch (e) {
+          return false;
+        }
+      };
+      fileReader.readAsBinaryString(files[0]);
+    }
+
+
+

整体代码

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+
+
<template>
+  <div>
+    <div class="container">
+      {{ upload_file || "导入" }}
+      <input
+        type="file"
+        accept=".xls,.xlsx"
+        class="upload_file"
+        @change="readExcel($event)"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import XLSX from "xlsx";
+
+export default {
+  data() {
+    return {
+      upload_file: "",
+      lists: []
+    };
+  },
+  methods: {
+    submit_form() {
+      // 给后端发送请求,更新数据
+      console.log("假装给后端发了个请求...");
+    },
+    readExcel(e) {
+      // 读取表格文件
+      let that = this;
+      const files = e.target.files;
+      if (files.length <= 0) {
+        return false;
+      } else if (!/\.(xls|xlsx)$/.test(files[0].name.toLowerCase())) {
+        this.$message({
+          message: "上传格式不正确,请上传xls或者xlsx格式",
+          type: "warning"
+        });
+        return false;
+      } else {
+        // 更新获取文件名
+        that.upload_file = files[0].name;
+      }
+
+      const fileReader = new FileReader();
+      fileReader.onload = ev => {
+        try {
+          const data = ev.target.result;
+          const workbook = XLSX.read(data, {
+            type: "binary"
+          });
+          const wsname = workbook.SheetNames[0]; //取第一张表
+          const ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]); //生成json表格内容
+          that.lists = [];
+          // 从解析出来的数据中提取相应的数据
+          ws.forEach(item => {
+            that.lists.push({
+              username: item["用户名"],
+              phone_number: item["手机号"]
+            });
+          });
+          // 给后端发请求
+          this.submit_form();
+        } catch (e) {
+          return false;
+        }
+      };
+      fileReader.readAsBinaryString(files[0]);
+    }
+  }
+};
+</script>
+
+
+

样式

+

原本的文件上传样式可能会跟页面整体风格不搭,所以需要修改其样式。不过此处并不是直接修改其样式而是通过写一个div来覆盖原有的上传按钮。此处样式与element UI中的primary按钮样式相同。

+

实现该样式的关键在于.upload_fileopacityposition

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
.container {
+  border: none;
+  border-radius: 4px;
+  background-color: #409eff;
+  height: 40px;
+  margin-top: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0 15px;
+  min-width: 80px;
+  *zoom: 1;
+}
+
+.upload_file {
+  font-size: 20px;
+  opacity: 0;
+  position: absolute;
+  filter: alpha(opacity=0);
+  width: 60px;
+}
+
+
+

最后

+

前端的日益强大导致很多功能都可以在前端去直接实现,并且可以减少服务器压力。

+

当然单纯地去实现这样的数据传输,尤其对于重要数据,是很不安全的,因此在前后端数据传输的时候,可以加上加密校验,这个后期会来写的。

+

参考文章

+

为了实现该功能参考了如下大佬的文章:

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue\346\214\211\351\234\200\345\274\225\345\205\245elementui\346\212\245\351\224\231error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/index.html" "b/p/vue\346\214\211\351\234\200\345\274\225\345\205\245elementui\346\212\245\351\224\231error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/index.html" new file mode 100644 index 0000000..e7fed97 --- /dev/null +++ "b/p/vue\346\214\211\351\234\200\345\274\225\345\205\245elementui\346\212\245\351\224\231error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/index.html" @@ -0,0 +1,660 @@ + + + + +Vue按需引入ElementUI报错Error -- Plugin -- Preset files are not allowed to export objects, only functions + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Vue按需引入ElementUI报错Error -- Plugin -- Preset files are not allowed to export objects, only functions +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

报错

+

按照ElementUI官方文档按需引入却报错,首先报错缺少babel-preset-es2015。安装该组件之后编译却报错。

+
+ +
+
1
+
+
Error: Plugin/Preset files are not allowed to export objects, only functions.
+
+
+

解决

+

该问题为babel版本冲突。

+

安装插件

+
+ +
+
1
+
+
yarn add @babel/preset-env 
+
+
+

编辑.babelrc

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
{  
+	"presets": [["@babel/preset-env", { "modules": false }]],
+    "plugins": [
+      [
+        "component",
+        {
+          "libraryName": "element-ui",
+          "styleLibraryName": "theme-chalk"
+        }
+      ]
+    ]
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue\347\246\201\346\255\242\351\201\256\347\275\251\345\261\202\344\270\213\347\232\204\351\241\265\351\235\242\346\273\232\345\212\250/index.html" "b/p/vue\347\246\201\346\255\242\351\201\256\347\275\251\345\261\202\344\270\213\347\232\204\351\241\265\351\235\242\346\273\232\345\212\250/index.html" new file mode 100644 index 0000000..4da2a3f --- /dev/null +++ "b/p/vue\347\246\201\346\255\242\351\201\256\347\275\251\345\261\202\344\270\213\347\232\204\351\241\265\351\235\242\346\273\232\345\212\250/index.html" @@ -0,0 +1,665 @@ + + + + +vue禁止遮罩层下的页面滚动 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ vue禁止遮罩层下的页面滚动 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

问题

+

功能开发过程中写遮罩时,遇到遮罩下页面还可以滚动的问题。

+

解决

+

直接给遮罩下的元素套上一个样式,使其不可滚动。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+
+
<template>
+	<div :class="isPopup ? 'disableRoll' : ''">
+    <div>
+      ...
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      isPopup: false,
+    }
+  }
+}
+</script>
+
+<style>
+  .disableRoll {
+    overflow: hidden;
+    position: fixed;
+    height: 100%;
+    width: 100%;
+  }
+</style>
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue\347\273\204\344\273\266props\345\217\214\345\220\221\347\273\221\345\256\232/index.html" "b/p/vue\347\273\204\344\273\266props\345\217\214\345\220\221\347\273\221\345\256\232/index.html" new file mode 100644 index 0000000..4f75177 --- /dev/null +++ "b/p/vue\347\273\204\344\273\266props\345\217\214\345\220\221\347\273\221\345\256\232/index.html" @@ -0,0 +1,846 @@ + + + + +vue组件props双向绑定 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ vue组件props双向绑定 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

在vue2中不允许子组件直接修改props,为单项数据流,所有若要修改只能通过额外的值,并监听props以改变额外的值。

+

设置props

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
props: {
+    dialog: {
+      type: Boolean,
+      default: false
+    }
+}
+
+
+

创建额外的值

+

data中创建一个localDialog,其值为this.dialog

+
+ +
+
1
+2
+3
+4
+5
+
+
data() {
+    return {
+    	localDialog: this.dialog
+    }
+}
+
+
+

监听

+

保持同步的关键在于需要在子组件内监听props,即此处的dialog

+
+ +
+
1
+2
+3
+4
+5
+
+
watch: {
+    dialog(val) {
+      this.localDialog = val
+    }
+}
+
+
+

子组件向父组件传递

+

子组件使用this.$emit()即可向父组件传递变化的值。

+
+ +
+
1
+2
+3
+4
+5
+
+
methods: {
+    sendToFather() {
+        this.$emit('dialogchange', this.localDialog)
+    }
+}
+
+
+

父组件调用

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<your-component :dialog="dialog" @dialogchange="dialogchange" />
+data() {
+	return {
+        dialog: false
+    }
+},
+methods: {
+    dialogchange(val) {
+    	this.dialog = val
+    }
+}
+
+
+

完整代码

+

子组件

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+
+
<template>
+    <div :visible="localDialog">
+        hunterji.com
+		<button @click="sendToFather" />
+    </div>
+</template>
+
+<script>
+export default {
+	props: {
+    	dialog: {
+      	type: Boolean,
+      	default: false
+    	}
+	},
+	data() {
+        return {
+            localDialog: this.dialog
+        }
+    },
+    watch: {
+   		dialog(val) {
+      		this.localDialog = val
+    	}
+	},
+    methods: {
+    	sendToFather() {
+        	this.$emit('dialogchange', this.localDialog)
+    	}
+	}
+}
+</script>
+
+
+

父组件

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
+
<template>
+    <your-component :dialog="dialog" @dialogchange="dialogchange" />
+</template>
+
+<script>
+import yourComponent from './yourComponent'
+
+export default {
+	components: {
+        yourComponent
+    },
+    data() {
+		return {
+        	dialog: false
+    	}
+	},
+	methods: {
+    	dialogchange(val) {
+    		this.dialog = val
+    	}
+	}
+}
+</script>
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/vue\351\241\271\347\233\256\345\233\275\351\231\205\345\214\226/index.html" "b/p/vue\351\241\271\347\233\256\345\233\275\351\231\205\345\214\226/index.html" new file mode 100644 index 0000000..1551d23 --- /dev/null +++ "b/p/vue\351\241\271\347\233\256\345\233\275\351\231\205\345\214\226/index.html" @@ -0,0 +1,1034 @@ + + + + +VUE项目国际化 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ VUE项目国际化 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

i18n是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。 在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。

+

Node.js本身有一个i18n的包,但是为了更好地结合vue,此处我们使用的是vue-i18n

+

源码:https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo

+

环境

+
    +
  • Vue 3.0.0
  • +
  • TypeScript 4.5.4
  • +
  • Vue Cli 4.5.15
  • +
+

安装

+

使用vue cli安装,会自动生成文件且引入。

+
+ +
+
1
+
+
vue add i18n
+
+
+

执行过程中将需要填写如下问题:

+
+ +
+
1
+2
+3
+4
+
+
? The locale of project localization. [默认选项]en
+? The fallback locale of project localization. [默认选项]en
+? The directory where store localization messages of project. It's stored under `src` directory. [默认选项]locales
+? Enable legacy API (compatible vue-i18n@v8.x) mode ? [默认选项]No
+
+
+

执行完成之后,将会自动处理如下文件:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
// vue add i18n执行结束后的git status
+
+new file:   .env // 执行过程中的第一个和第二个问题将使用环境变量更新
+modified:   package.json
+new file:   src/components/HelloI18n.vue // 官方给出的demo组件
+new file:   src/i18n.ts // 自动读取多语言的json文件且创建i18n实例
+new file:   src/locales/en.json // 多语言json文件所在文件夹,默认选择的en语言,所以生成一个默认的语言json文件
+modified:   src/main.ts // 引入i18n
+new file:   vue.config.js // 配置i18n
+modified:   yarn.lock
+
+
+

多语言配置

+

想要多语言则需要配置多个对应语言的json文件,其字段必须相同,否则当用户切换时,会出现找不到该字段对应文字的问题。

+

为了便于切换和处理,此处不再使用en之类的简写,而是和系统语言名称对应起来,此处将使用zh_CNen_GB来做配置。

+

创建多语言json文件

+

此处生成两个文件src/locales/zh_CN.jsonsrc/locales/en_GB.json,内容分别如下:

+
+ +
+
1
+2
+3
+4
+
+
{
+  "language": "中文",
+  "message": "你好,世界!"
+}
+
+
+
+ +
+
1
+2
+3
+4
+
+
{
+  "language": "English",
+  "message": "Hello, World !"
+}
+
+
+

更新语言标识

+

将相关文件的en都改成en_GB

+
+ +
+
1
+2
+3
+4
+
+
##env
+
+VUE_APP_I18N_LOCALE=en_GB
+VUE_APP_I18N_FALLBACK_LOCALE=en_GB
+
+
+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
// src/i18n.ts
+
+// ...
+export default createI18n({
+  legacy: false,
+  locale: process.env.VUE_APP_I18N_LOCALE || 'en_GB',
+  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en_GB',
+  messages: loadLocaleMessages()
+})
+
+
+

使用

+

重写首页

+

此处重写src/views/Home.vue文件,先按照正常的内容去写,内容如下:

+
+ +
+
1
+2
+3
+4
+5
+
+
<template>
+  <div>
+    <div>Hello, World !</div>
+  </div>
+</template>
+
+
+

配置多语言

+

需要配置多语言的地方就是展示的文字,所以将该文字替换掉即可。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<template>
+  <div>
+    <div>{{ t('message') }}</div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+</script>
+
+
+

此处引入t方法,而其参数message为上面配置的每一个json文件中的message字段,其将用json对应字段的value来展示。

+

可能有小伙伴儿要问,那如果不是html中的展示文字怎么办呢?

+

这也一样使用t方法的,示例如下:

+
+ +
+
1
+
+
<input type="text" :placeholder="t('message')" />
+
+
+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
<script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+
+const exampleVal = t('message')
+</script>
+
+
+

此处我们的json结构都是单层的,如果是嵌套的结构的话,可以使用t('home.message')这样的方式来引用。

+

切换语言

+

此处需要引入locale,主要通过更新locale.value来切换语言。此处的切换是全局的,所以只需要写一个切换组件,其它组件都将会被切换语言。

+

写一个下拉框来实现语言的切换,完整内容如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
+
<template>
+  <div>
+    <div>{{ t('message') }}</div>
+    <select v-model="state.language" @change="handleLanguageChange">
+      <option value="zh_CN">zh_CN</option>
+      <option value="en_GB">en_GB</option>
+    </select>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t, locale } = useI18n()
+const state: { language: string } = reactive({
+  language: 'en_GB'
+})
+const handleLanguageChange = (option: { target: { value: string } }) => {
+  // 主要通过更新locale.value来切换语言
+  locale.value = option.target.value
+}
+</script>
+
+
+

通过下拉框切换选项可以切换语言。

+

本地环境

+

虽然i18n设置了默认的语言,但是友好的交互应当是根据用户的环境语言加载。这里需要使用navigator.language来拿到用户的环境语言,通过判断环境语言切换初始的语言配置。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
switch (navigator.language) {
+  case 'zh-CN':
+    state.language = 'zh_CN'
+    locale.value = 'zh_CN'
+    break
+  case 'en-GB':
+    state.language = 'en_GB'
+    locale.value = 'en_GB'
+    break
+  default:
+    state.language = 'en_GB'
+    locale.value = 'en_GB'
+}
+
+
+

完整代码如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+
+
<template>
+  <div>
+    <div>{{ t('message') }}</div>
+    <select v-model="state.language" @change="handleLanguageChange">
+      <option value="zh_CN">zh_CN</option>
+      <option value="en_GB">en_GB</option>
+    </select>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t, locale } = useI18n()
+const state: { language: string } = reactive({
+  language: 'en_GB'
+})
+const handleLanguageChange = (option: { target: { value: string } }) => {
+  // 主要通过更新locale.value来切换语言
+  locale.value = option.target.value
+}
+const fetchData = () => {
+  switch (navigator.language) {
+    // 环境语言中间的线是居中的
+    case 'zh-CN':
+      state.language = 'zh_CN'
+      locale.value = 'zh_CN'
+      break
+    case 'en-GB':
+      state.language = 'en_GB'
+      locale.value = 'en_GB'
+      break
+    default:
+      state.language = 'en_GB'
+      locale.value = 'en_GB'
+  }
+}
+
+onMounted(() => {
+  fetchData()
+})
+</script>
+
+
+

运行之后,默认语言不再是英语,而是跟环境语言一致的。

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover.jpg" "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover.jpg" new file mode 100644 index 0000000..4cbdcfe Binary files /dev/null and "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover.jpg" differ diff --git "a/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_120x120_fill_q75_box_smart1.jpg" "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_120x120_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..a0d9f48 Binary files /dev/null and "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_120x120_fill_q75_box_smart1.jpg" differ diff --git "a/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_1600x0_resize_q75_box.jpg" "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_1600x0_resize_q75_box.jpg" new file mode 100644 index 0000000..b1b1d97 Binary files /dev/null and "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_1600x0_resize_q75_box.jpg" differ diff --git "a/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_800x0_resize_q75_box.jpg" "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_800x0_resize_q75_box.jpg" new file mode 100644 index 0000000..e70f1ba Binary files /dev/null and "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/cover_hu18c5fb7037479757471f623d2395d33e_660360_800x0_resize_q75_box.jpg" differ diff --git "a/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/index.html" "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/index.html" new file mode 100644 index 0000000..a5f11d5 --- /dev/null +++ "b/p/webassembly--\346\234\252\346\235\245\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\345\277\205\345\244\207\346\212\200\350\203\275/index.html" @@ -0,0 +1,1513 @@ + + + + +WebAssembly -- 未来前端开发的必备技能 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post WebAssembly -- 未来前端开发的必备技能 + + +
+ + +
+ + +
+

+ WebAssembly -- 未来前端开发的必备技能 +

+ + +

+ WebAssembly是一种新的编码方式,可以在现代的网络浏览器中运行,它也被设计为可以与 JavaScript 共存,允许两者一起工作。 +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。

+

快速上手

+

用go写一个hello world

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
package main
+
+import (
+	"fmt"
+)
+
+func main() {
+	fmt.Println("Hello, WebAssembly!")
+}
+
+
+

将go文件编译成wasm文件

+
+ +
+
1
+
+
GOOS=js GOARCH=wasm go build -o static/main.wasm
+
+
+

拷贝出wasm_exec.js

+

该文件为go的wasm的js支持文件

+
+ +
+
1
+
+
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" static
+
+
+

html文件调用wasm文件

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
<script src="static/wasm_exec.js"></script>
+<script>
+  const go = new Go();
+  WebAssembly.instantiateStreaming(fetch("static/main.wasm"), go.importObject)
+    .then((result) => go.run(result.instance))
+</script>
+
+
+

验证调用

+

浏览器加载html文件,f12打开控制台,可以看到wasm的打印消息。

+

go与js的类型转换

+

类型映射

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
| Go                     | JavaScript             |
+| ---------------------- | ---------------------- |
+| js.Value               | [its value]            |
+| js.Func                | function               |
+| nil                    | null                   |
+| bool                   | boolean                |
+| integers and floats    | number                 |
+| string                 | string                 |
+| []interface{}          | new array              |
+| map[string]interface{} | new object             |
+
+
+

如上为官方给出的go与js的类型映射表。

+

比如在go中调用js函数,参数为array,那么就可以直接将go的[]interface{}类型的变量作为参数使用。

+

函数转换数组

+

syscall/js提供了两个函数:

+
    +
  • CopyBytesToGo:func CopyBytesToGo(dst []byte, src Value) int
  • +
  • CopyBytesToJS:func CopyBytesToJS(dst Value, src []byte) int
  • +
+

两者对于go而言,类型都是[]byte,但是对于js而言,需要Uint8Array或者Uint8ClampedArray类型,否则就会报错。

+

那么,如何在go中生成一个Uint8Array或者Uint8ClampedArray类型的变量呢?官方的类型映射表也没有啊…那么就看下一步。

+

其余类型

+

对于非官方类型映射表内的类型,和官方提供的两个数据类型转换之外的类型,可以通过一种通用的方式来生成,以上一步的Uint8Array为例:

+
+ +
+
1
+
+
js.Global().Get("Uint8Array").New(<length>)
+
+
+

实际使用案例:

+
+ +
+
1
+2
+3
+
+
// goData []byte{...}
+jsData := js.Global().Get("Uint8Array").New(len(goData))
+js.CopyBytesToJS(jsData, goData)
+
+
+

那么,比如js中的Date类型:

+
+ +
+
1
+2
+
+
dateConstructor := js.Global().Get("Date")
+dateConstructor.New("2020-10-01")
+
+
+

极端情况

+

好吧,还有最后一个方案,如果遇到极端情况,上述方案都无法解决,那么请转换成字符串吧!让go和js用各自的方法分别处理一波,得到自己想要的结果或者给出各自想给的数据。

+

js调用go函数

+
+

此处需要在go中引入syscall/js,以实现js相关的操作。

+
+

注册go函数

+

将go的函数注册为js的函数,由js来进行调用。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
package main
+
+import "syscall/js"
+
+func handleCount(this js.Value, args []js.Value) interface{} {
+  count := args[0].Int()
+  return js.ValueOf(count + 1)
+}
+
+func main() {
+	done := make(chan string, 0)
+	js.Global().Set("HandleEvent", js.FuncOf(handleEvent))
+	<-done
+}
+
+
+

js.Func() 接受一个函数类型作为其参数,该函数的定义是固定的:

+
+ +
+
1
+2
+3
+4
+
+
func(this Value, args []Value) interface{}
+// this 即 JavaScript 中的 this
+// args 是在 JavaScript 中调用该函数的参数列表。
+// 返回值需用 js.ValueOf 映射成 JavaScript 的值
+
+
+

js.ValueOf返回作为js的值:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
| Go                     | JavaScript             |
+| ---------------------- | ---------------------- |
+| js.Value               | [its value]            |
+| js.Func                | function               |
+| nil                    | null                   |
+| bool                   | boolean                |
+| integers and floats    | number                 |
+| string                 | string                 |
+| []interface{}          | new array              |
+| map[string]interface{} | new object             |
+
+
+

js调用

+

在js中使用也非常简单,引入wasm文件之后,直接调用函数即可。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<script src="static/wasm_exec.js"></script>
+<script>
+  const go = new Go();
+  WebAssembly.instantiateStreaming(fetch("static/main.wasm"), go.importObject)
+    .then((result) => go.run(result.instance))
+</script>
+<script>
+	document.querySelector('#button').addEventListener('click', () => {
+  	HandleEvent(1) // 传入参数1
+	})
+</script>
+
+
+

go调用js函数

+

如果在js中本身已经定义了函数,那么在go中也可以直接调用该函数,进行运算,将得出的结果在go中继续使用。

+

定义js函数

+
+ +
+
1
+2
+3
+4
+5
+
+
<script>
+    function add(m, n) {
+        return m + n
+    }
+</script>
+
+
+

go中调用js函数

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
package main
+
+import (
+	"fmt"
+	"syscall/js"
+)
+
+func main() {
+	result := js.Global().Call("add", 1, 2)
+	fmt.Println(result) // 此处输出类型为js.Value,无法直接使用
+  fmt.Println(result.Int() + 1) // 使用.Int()将其转换为go中的类型,即可直接使用
+}
+
+
+

引入wasm

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<script>
+    function add(m, n) {
+        return m + n
+    }
+</script>
+<script src="static/wasm_exec.js"></script>
+<script>
+    const go = new Go();
+    WebAssembly.instantiateStreaming(fetch("static/main.wasm"), go.importObject)
+        .then((result) => go.run(result.instance))
+</script>
+
+
+

结果

+

在前端调试台可以看到输出:

+
+ +
+
1
+2
+
+
<number: 3>
+4
+
+
+

第一个结果就是js.Value的值,第二个结果则是转换成go的值,并按照逻辑进行了+1处理。

+

回调函数/解决go函数阻塞问题

+
+

The Go function fn is called with the value of JavaScript’s “this” keyword and the arguments of the invocation. The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.

+

Invoking the wrapped Go function from JavaScript will pause the event loop and spawn a new goroutine. Other wrapped functions which are triggered during a call from Go to JavaScript get executed on the same goroutine.

+

As a consequence, if one wrapped function blocks, JavaScript’s event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.

+
+

syscall/js官方文档表明,如果go包装函数阻塞,那么js的事件循环也将被阻塞,直到函数返回,调用任何需要事件循环(如fetch)的异步js api都导致立即死锁。因此,一个阻塞函数应该显式地启动一个新的协程。

+

此处,可以在go中注册一个回调函数,加上协程实现异步,不会产生堵塞。

+

注册函数

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+
+
package main
+
+import (
+	"fmt"
+	"syscall/js"
+	"time"
+)
+
+func handleRender(this js.Value, args []js.Value) interface{} {
+	username := args[0].String()
+	callback := args[len(args)-1]
+
+	go func() {
+		time.Sleep(3 * time.Second)
+		callback.Invoke(fmt.Sprintf("hello, %s !", username))
+	}()
+
+	fmt.Println("waiting...")
+	return nil
+}
+
+func main() {
+	done := make(chan string, 0)
+	js.Global().Set("HandleRender", js.FuncOf(handleRender))
+	<-done
+}
+
+
+

js调用

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<script src="static/wasm_exec.js"></script>
+<script>
+    const go = new Go();
+    WebAssembly.instantiateStreaming(fetch("static/main.wasm"), go.importObject)
+        .then((result) => go.run(result.instance))
+</script>
+<script>
+    document.querySelector('#button').addEventListener('click', () => {
+        HandleRender("tom", (message) => console.log('message: ', message))
+    })
+</script>
+
+
+

输出

+

在浏览器调试台,可以看到:

+
+ +
+
1
+2
+
+
waiting... // 先输出了waiting...
+hello, tom ! // 隔了3秒之后,输出了回调函数的值
+
+
+

Go实现Promise

+

上一步的回调函数,解决了函数阻塞问题,此处,结合回调函数实现promise,来丰富异步场景。

+

在js中,promise是这样的:

+
+ +
+
1
+2
+3
+4
+5
+
+
const message = new Promise((resolve, reject) => {
+    setTimeout(() => {
+        resolve("hello, world !")
+    }, 3000)
+})
+
+
+

使用asyncawait调用,拿到结果:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
async function printMessage() {
+    const message = new Promise((resolve, reject) => {
+        setTimeout(() => {
+            resolve("hello, world !")
+        }, 3000)
+    })
+
+    const result = await message
+    console.log(result)
+}
+
+
+

在go中又如何构建promise呢?这里可以用到上述go与js的类型转换,创建一个promise:

+
+ +
+
1
+
+
js.Global().Get("Promise")
+
+
+

注册函数

+

go的完整实现如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+
+
package main
+
+import (
+	"fmt"
+	"syscall/js"
+	"time"
+)
+
+var document = js.Global().Get("document")
+
+func handleRender(this js.Value, args []js.Value) interface{} {
+
+	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+
+		resolve := args[0]
+
+		go func() {
+			time.Sleep(3 * time.Second)
+			resolve.Invoke("hello, world !")
+		}()
+
+		fmt.Println("waiting...")
+		return nil
+	})
+
+	promiseConstructor := js.Global().Get("Promise")
+	return promiseConstructor.New(handler)
+}
+
+func main() {
+	done := make(chan string, 0)
+	js.Global().Set("HandleRender", js.FuncOf(handleRender))
+	<-done
+}
+
+
+

js调用

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
<script src="static/wasm_exec.js"></script>
+<script>
+    const go = new Go();
+    WebAssembly.instantiateStreaming(fetch("static/main.wasm"), go.importObject)
+        .then((result) => go.run(result.instance))
+</script>
+<script>
+    document.querySelector('#button').addEventListener('click', async () => {
+        const message = await HandleRender()
+        console.log('message: ', message)
+    })
+</script>
+
+
+

输出

+

在浏览器调试台,可以看到:

+
+ +
+
1
+2
+
+
waiting... // 先输出了waiting...
+message:  hello, world ! // 隔了3秒之后输出
+
+
+

操作DOM

+

使用document

+

定义一个全局的document

+
+ +
+
1
+
+
var docuemnt = js.Global().Get("document")
+
+
+

获取元素

+

获取一个idcontainerdiv,设置background-color: redwidht: 600height: 400

+
+ +
+
1
+2
+3
+4
+5
+
+
var containerElement = document.Call("getElementById", "container")
+var containerElementStyle = container.Get("style")
+containerElementStyle.Set("background", "red")
+containerElementStyle.Set("width", "600px")
+containerElementStyle.Set("height", "400px")
+
+
+

创建元素

+

创建一个idimageimage,设置width:300height:200

+
+ +
+
1
+2
+3
+
+
var imageElement = document.Call("createElement", "canvas")
+imageElement.Set("width", 300)
+imageElement.Set("width", 200)
+
+
+

添加子元素

+

image添加为idcontainerdiv的子元素

+
+ +
+
1
+
+
containerElement.Call("appendChild", imageElement)
+
+
+

添加事件

+

image添加右击事件,右击image则阻止右键菜单

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
// 定义响应函数
+func handlePreventEventCallBack(this js.Value, args []js.Value) interface{} {
+	args[0].Call("preventDefault")
+	return false
+}
+
+// 给image添加事件
+imageElement.Call("addEventListener", "contextmenu", js.FuncOf(handlePreventEventCallBack))
+
+
+

这里需要注意的是,当不再调用响应事件函数时,必须调用Func.Release以释放资源:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
var cb js.Func
+cb = js.FuncOf(func(this js.Value, args []js.Value) any {
+	fmt.Println("button clicked")
+	cb.Release() // 如果不再单击该按钮,则释放该函数
+	return nil
+})
+js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb)
+
+
+

Canvas

+

这里放一波canvas的案例,包含了一些常用方法,可以参考完成更多操作。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+
+
package main
+
+import (
+	"math"
+	"syscall/js"
+)
+
+const (
+	width  = 400
+	height = 400
+	radius = 200
+)
+
+var document = js.Global().Get("document")
+
+func handleRender(this js.Value, args []js.Value) interface{} {
+	var canvas = document.Call("getElementById", "canvas")
+	canvas.Set("width", width)
+	canvas.Set("height", height)
+
+	var ctx = canvas.Call("getContext", "2d")
+
+	ctx.Call("beginPath")
+	ctx.Call("arc", width/2, height/2, radius, 0, 2*math.Pi)
+	ctx.Set("fillStyle", "lightpink")
+	ctx.Call("fill")
+	ctx.Set("lineWidth", 2)
+	ctx.Set("strokeStyle", "red")
+	ctx.Call("stroke")
+
+	ctx.Set("font", "20px Comic Sans MS")
+	ctx.Set("fillStyle", "blue")
+	ctx.Call("fillText", "Hello, World !", width/2-60, height/2)
+
+	return nil
+}
+
+func main() {
+	done := make(chan string, 0)
+	js.Global().Set("HandleRender", js.FuncOf(handleRender))
+	<-done
+}
+
+
+

渲染结果:

+

go_wasm_canvas

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/wow.js\345\222\214animate-css\345\234\250vue3\344\270\255\347\232\204\345\272\224\347\224\250/index.html" "b/p/wow.js\345\222\214animate-css\345\234\250vue3\344\270\255\347\232\204\345\272\224\347\224\250/index.html" new file mode 100644 index 0000000..8f9a40f --- /dev/null +++ "b/p/wow.js\345\222\214animate-css\345\234\250vue3\344\270\255\347\232\204\345\272\224\347\224\250/index.html" @@ -0,0 +1,790 @@ + + + + +wow.js和animate css在vue3中的应用 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ wow.js和animate css在vue3中的应用 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

环境

+
    +
  • vue 3.2
  • +
  • typescript 4.7.4
  • +
  • wow.js 1.2.2
  • +
  • animate.css 4.1.1
  • +
+

animate.css

+

下载

+
+ +
+
1
+
+
pnpm add animate.css -D
+
+
+

引入

+

vue3项目的main.ts中引入

+
+ +
+
1
+
+
import 'animate.css'
+
+
+

使用

+

需要注意的是,animate css在4.0之后使用animate__前缀

+
+ +
+
1
+
+
<h1 class="animate__animated animate__bounce">An animated element</h1>
+
+
+

动画延迟

+

官方方法

+

官方给出的动画延迟是animate__delay-2sanimate__delay-3s ……

+

直接在class中添加即可

+
+ +
+
1
+
+
<h1 class="animate__animated animate__bounce animate__delay-2s">An animated element</h1>
+
+
+

自定义延迟

+

特殊场景需要使用不同于官方的延迟时间,因此可以自定义延迟时间,直接声明延迟的类,然后在class上加入即可

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
.animation-delay-1 {
+  animation-delay: 100ms;
+}
+.animation-delay-2 {
+  animation-delay: 300ms;
+}
+.animation-delay-3 {
+  animation-delay: 500ms;
+}
+.animation-delay-4 {
+  animation-delay: 700ms;
+}
+.animation-delay-5 {
+  animation-delay: 900ms;
+}
+
+
+

使用

+
+ +
+
1
+2
+
+
<h1 class="animate__animated animate__bounce animation-delay-1">An animated element</h1>
+<h1 class="animate__animated animate__bounce animation-delay-2">Another animated element</h1>
+
+
+

wow.js

+

下载

+
+ +
+
1
+
+
pnpm add wow.js -D
+
+
+

引入

+

vue3项目的main.ts中引入,内容如下:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
import WOW from 'wow.js'
+
+new WOW({
+  boxClass: 'wow', // 类名,在用户滚动时显示隐藏的框。
+  animateClass: 'animate__animated', // 触发CSS动画的类名称
+  offset: 300, // 定义浏览器视口底部与隐藏框顶部之间的距离。当用户滚动并到达此距离时,隐藏的框会显示出来。
+  mobile: true, // 在移动设备上打开/关闭WOW.js。
+  live: true, // 在页面上同时检查新的WOW元素。
+}).init()
+
+
+

使用

+

使用wow直接替代animate__animated即可

+
+ +
+
1
+
+
<h1 class="wow animate__bounce animation-delay-1">An animated element</h1>
+
+
+

结语

+

由于写一个页面需要使用到wow,好多年没用过了,查了一下文档超多版本教程,使用起来各种不成功,难受…暂时也没找到可替代的方案…

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\344\275\240\346\230\257\344\270\252\346\210\220\347\206\237\347\232\204\344\273\243\347\240\201\350\246\201\345\255\246\344\274\232\350\207\252\345\267\261\346\214\211\351\234\200\345\274\225\345\205\245\344\272\206/index.html" "b/p/\344\275\240\346\230\257\344\270\252\346\210\220\347\206\237\347\232\204\344\273\243\347\240\201\350\246\201\345\255\246\344\274\232\350\207\252\345\267\261\346\214\211\351\234\200\345\274\225\345\205\245\344\272\206/index.html" new file mode 100644 index 0000000..4800fd0 --- /dev/null +++ "b/p/\344\275\240\346\230\257\344\270\252\346\210\220\347\206\237\347\232\204\344\273\243\347\240\201\350\246\201\345\255\246\344\274\232\350\207\252\345\267\261\346\214\211\351\234\200\345\274\225\345\205\245\344\272\206/index.html" @@ -0,0 +1,825 @@ + + + + +你是个成熟的代码要学会自己按需引入了 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ 你是个成熟的代码要学会自己按需引入了 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

前端小伙伴儿们是不是经常遇到ui组件全局引入导致体积太大,按需引入导致不断手写会很麻烦。所以,当当!今天我们来让代码自己按需引入,解放前端小伙伴儿们的生产力,早日实现下班自由!(甲方:我要再改十个需求!)

+

介绍

+

我们这里介绍的是unplugin-vue-components。该组件是由vue核心开发成员antfu开发的,尤大也是推荐的,且是该项目的金牌赞助商。

+

该组件主要是为了实现vue项目的组件自动引入。

+

官方文档:antfu/unplugin-vue-components

+

完整案例

+

此处我们以vite为例,来主要看一下其对于自定义组件和UI库组件的自动按需引入。

+

源码:https://github.com/Kuari/Blog/tree/master/Examples/unplugin_auto_import

+

创建项目

+
+ +
+
1
+
+
yarn create vite unplugin_auto_import --template vue
+
+
+

然后进入文件夹安装依赖。

+
+ +
+
1
+2
+
+
cd unplugin_auto_import
+yarn install
+
+
+

安装unplugin-vue-components

+
+ +
+
1
+
+
yarn add -D unplugin-vue-components
+
+
+

配置vite.config.js

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import Components from 'unplugin-vue-components/vite' // 新增
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    vue(),
+    Components({ /* options */ }) // 新增
+  ]
+})
+
+
+

自动引入自定义组件

+

我们默认模板创建的项目中,默认在App.vue中引入了./components/HelloWorld.vue。此处就可以来尝试下如何自动引入了。

+

在配置了unplugin-vue-components之后,现在只需要删除引入行(其实这时候打开vscode就会发现改行已经灰掉了),被删除行如下所示:

+
+ +
+
1
+
+
import HelloWorld from './components/HelloWorld.vue'
+
+
+

删除以后完整App.vue如下所示:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
+
<script setup>
+</script>
+
+<template>
+  <img alt="Vue logo" src="./assets/logo.png" />
+  <HelloWorld msg="Hello Vue 3 + Vite" />
+</template>
+
+<style>
+#app {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2c3e50;
+  margin-top: 60px;
+}
+</style>
+
+
+

现在在命令行中运行yarn dev,再打开浏览器查看http://localhost:3000页面,是不是发现,哎?!居然引入了!(心中狂喜,要早日实现下班自由了)

+截屏2021-10-27 下午2.47.55 +

自动引入UI库组件

+

这里以element plus为例。

+

首先是安装element plus。官方文档也没说要装,我还以为内置呢,一直报错,有点懵逼,哈哈。

+
+ +
+
1
+
+
yarn add -D element-plus
+
+
+

引入element plus的resolver,此处编辑vite.config.js文件,修改后如下所示:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import Components from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // 引入ElementPlusResolver
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    vue(),
+    Components({resolvers: [ElementPlusResolver()]}) // 添加配置
+  ]
+})
+
+
+

那么现在神奇的事情来了,就可以直接使用UI库的组件了!

+

我们在App.vue中使用一个el-button组件试试。我们在App.vue中加入如下行:

+
+ +
+
1
+
+
<el-button type="primary">Kuari</el-button>
+
+
+

添加后App.vue上下代码如下:

+
+ +
+
1
+2
+3
+4
+5
+
+
<template>
+  <img alt="Vue logo" src="./assets/logo.png" />
+  <HelloWorld msg="Hello Vue 3 + Vite" />
+  <el-button type="primary">Kuari</el-button>
+</template>
+
+
+

现在,运行yarn dev,打开浏览器,可以看到,就直接可以使用UI库的组件了。

+截屏2021-10-27 下午2.59.06 +

打包

+

按需引入的功能并不是仅仅在开发时候的,在打包时,该组件也是Tree-shakable的,只会将你用了的组件打包。

+

按照当前教程所写的项目,当全局引入的时候,打包的dist文件夹为1.1MB,而使用该组件之后打包,其dist文件夹为202KB。

+

最后

+

手动按需导入是不可能手动按需导入的,这辈子都不可能了/狗头。

+

毕竟是大佬开发的强力工具,希望前端小伙伴儿们早日实现下班自由。

+ +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..7aa0993 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..94b09fa Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" new file mode 100644 index 0000000..5606cc4 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" new file mode 100644 index 0000000..e1bd93d Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/index.html" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/index.html" new file mode 100644 index 0000000..ae22b0a --- /dev/null +++ "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\200\345\277\253\351\200\237\345\274\200\345\247\213/index.html" @@ -0,0 +1,1024 @@ + + + + +使用Rust和WebAssembly整花活儿(一)——快速开始 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 使用Rust和WebAssembly整花活儿(一)——快速开始 + + +
+ + +
+ + +
+

+ 使用Rust和WebAssembly整花活儿(一)——快速开始 +

+ + +

+ WebAssembly可以使用多种语言开发, 如果你更注重性能和安全性, 那么使用Rust整花活儿是个更好的选择~ +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。

+

之前写过一篇文章,是关于如何使用golang来开发WebAssembly的——WebAssembly:未来前端开发的必备技能

+

Rust和Go都可以用来开发WebAssembly,但它们有各自的优势和劣势。

+

Rust的优点:

+
    +
  • 更快的性能和更小的二进制文件
  • +
  • 更好的内存安全性
  • +
+

Go的优点:

+
    +
  • 更容易上手和学习
  • +
  • 更好的生态系统和社区支持
  • +
+

综合来说,如果你更注重性能和内存安全性,那么Rust可能是更好的选择。而如果你更注重开发效率和易用性,那么Go可能更适合你。当然,实际情况还需要根据具体的项目需求和团队情况来选择。

+

因为一些工作需求,最近整了些rust的花活儿,这里系统地记录一下。当你遇到更注重性能和内存安全性的场景,希望这能有帮助。

+

环境

+
    +
  • Rust 1.70.0
  • +
  • wasm-bindgen 0.2.87
  • +
+

创建项目并添加依赖

+

此处默认已经安装Rust,需要安装的小伙伴儿可以参考官网

+

使用Cargo创建一个名为hello-wasm的项目:

+
+ +
+
1
+
+
cargo new --lib hello-wasm
+
+
+

进入项目,打开文件Cargo.toml,添加依赖:

+
+ +
+
1
+2
+3
+4
+5
+
+
[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+wasm-bindgen = "0.2.87"
+
+
+

更新lib.rs

+

默认创建项目中,存在一个名为lib.rs的文件,将内容全部替换成:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+pub fn add(a: i32, b: i32) -> i32 {
+  a + b
+}
+
+
+

编译

+

至此,我们创建了一个最简单的功能——一个返回两个整数相加结果的函数。

+

然后我们可以进行编译了,编译之前需要安装一个工具wasm-pack

+
+ +
+
1
+
+
cargo install wasm-pack
+
+
+

然后进行编译:

+
+ +
+
1
+
+
wasm-pack build --target web
+
+
+

编译完成之后,将会多出来一个pkg文件夹,内容如下:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
pkg
+├── hello_wasm.d.ts
+├── hello_wasm.js
+├── hello_wasm_bg.wasm
+├── hello_wasm_bg.wasm.d.ts
+└── package.json
+
+
+

虽然文件很多,但是首先我们看到了我们所需要的wasm文件,并且,根据go的wasm引入方式,这里我们或许会需要用到js文件。

+

前端引入

+

为了方便最快校验,直接在hello-wasm项目中创建index.html文件,来进行前端引入。

+

创建index.html

+

那么,首先,创建index.html文件:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>使用Rust和WebAssembly整花活儿</title>
+</head>
+
+<body>
+    Hello, World!
+</body>
+
+</html>
+
+
+

是的,没错!这是一场标准的开局!😼

+

引入WASM

+

其实不同于go语言的wasm引入方式,Rust更希望直接引入js文件,而不是让开发者手动引入wasm文件。

+

这里使用js引入:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<script type="module">
+    import init, { add } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+        const result = add(1, 2);
+        console.log(`the result from rust is: ${result}`);
+    }
+
+    run();
+</script>
+
+
+

完整代码

+

完整的html代码如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+
+
<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>使用Rust和WebAssembly整花活儿</title>
+</head>
+
+<body>
+    Hello, World!
+</body>
+
+<script type="module">
+    import init, { add } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+        const result = add(1, 2);
+        console.log(`the result from rust is: ${result}`);
+    }
+
+    run();
+</script>
+
+</html>
+
+
+

验证

+

这里可以快速起一个http server,这里我选择使用http-server,也可以使用python3 -m http.server这样的方式,看怎么各自的使用习惯。

+

那么,启动http server:

+
+ +
+
1
+
+
http-server
+
+
+

打开浏览器,访问http://localhost:8080,打开调试器,即可看到输出the result from rust is: 3,这就意味着迈出了整花活儿的第一步!

+

rust_wasm

+

常见问题

+

前端报响应类型错误

+

详细报错如下:

+
+ +
+
1
+
+
Failed to load module script: The server responded with a non-JavaScript MIME type of "application/wasm". Strict MIME type checking is enforced for module scripts per HTML spec.
+
+
+

当引入WebAssembly生成的js文件时,可能会遇到这个报错。报错乍一看是http server的响应问题,或者搜索时候,也会有帖子说这是一个response问题。

+

实际上,当按照这个文章一步步操作时是不会有这个问题的,是因为本文的编译参数是直接解决了这个问题的。当我自己摸索的时候,解决这个问题真的是看到人都麻了……

+

关键在于编译命令的参数:--target

+

当没有设置这个参数时,默认的参数其实是--target bundler,其是编译成给webpack之类的脚手架使用的。因此这里使用—target web,则是使其编译成可直接在web中使用。

+

相关参数如下:

+
    +
  • bundler:编译成给webpack之类的脚手架使用
  • +
  • web:编译成web可直接使用
  • +
  • nodejs:编译成可通过require来加载的node模块
  • +
  • deno:编译成可通过import加载的deno模块
  • +
  • no-modules:跟web类似,但是更旧,且不能使用es模块
  • +
+

直接引入wasm文件

+

若此时尝试直接引入wasm文件,而不是使用本文所述的方式,那么你会发现,也是可行的!

+
+ +
+
1
+2
+3
+4
+5
+
+
<script type="module">
+    WebAssembly.instantiateStreaming(fetch("./pkg/hello_wasm_bg.wasm"), {}).then(
+        (obj) => console.log('the result from rust is: ', obj.instance.exports.add(1, 2))
+    );
+</script>
+
+
+

是的,没错,当前是可行的,但是当引入了一些别的比如dom之类的,就坏起来了……

+

更新的wasm引入方式

+

上一问题中,且不说是否可以直接引入wasm文件,这里仅说一下,instantiateStreaming这个方法。这是一个更新的方法,无需转成arrayBuffer,这也是摸索Rust整活儿时候发现的。如果在别的语言引入wasm,请使用这个更新的方法吧。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..7aa0993 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..94b09fa Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" new file mode 100644 index 0000000..5606cc4 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" new file mode 100644 index 0000000..e1bd93d Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/index.html" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/index.html" new file mode 100644 index 0000000..c8f4c81 --- /dev/null +++ "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\270\211rust\344\270\216js\344\272\244\344\272\222/index.html" @@ -0,0 +1,1162 @@ + + + + +使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + + +
+ + +
+ + +
+

+ 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 +

+ + +

+ WebAssembly可以使用多种语言开发, 如果你更注重性能和安全性, 那么使用Rust整花活儿是个更好的选择~ +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

在上一篇文章《使用Rust和WebAssembly整花活儿(二)——DOM和类型转换》中,描述了使用Rust操作DOM,并实现Rust与JS类型转换的多种方法。

+

在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。

+

基于上一篇文章中,Rust与JS的类型转换的多种方法,本篇文章继续深入Rust与JS的交互。

+

首先,Rust与JS的类型转换,可以实现变量的传递,那么变量是要用在哪里呢?那必然是函数了!

+

所以,本篇文章来讲述一下Rust与JS的函数相互调用,基于此,可以实现大量日常功能开发。

+

并且,还将会讲述一下,如何导出Rust的struct给JS调用。

+

是的,没错,在JS调用Rust的struct!一开始看到这个功能的时候,我的脑子是有点炸裂的……😳

+

本篇文章中,将基于上一篇文章中创建的项目来继续开发。

+

源码:github.com/Kuari/hello-wasm

+

环境

+
    +
  • Rust 1.70.0
  • +
  • wasm-bindgen 0.2.87
  • +
+

函数的相互调用

+

JS调用Rust函数

+

其实,在本系列文章的第一篇中,就是使用的JS调用Rust函数作为案例来演示的,这里依然以此为例,主要讲一下要点。

+

首先,声明一个Rust函数:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+pub fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+
+
+

此处需要注意的要点如下:

+
    +
  • 引入wasm_bindgen
  • +
  • 声明一个函数,使用pub声明
  • +
  • 在函数上使用#[wasm_bindgen]宏来将Rust函数导出为WebAssembly模块的函数
  • +
+

接着,编译成wasm文件:

+
+ +
+
1
+
+
wasm-pack build --target web
+
+
+

然后,在JS中调用该函数:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<script type="module">
+    import init, { add } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+        const result = add(1, 2);
+        console.log(`the result from rust is: ${result}`);
+    }
+
+    run();
+</script>
+
+
+

最后,启动http server,在浏览器的控制台中可以看到the result from rust is: 3,表明调用成功!

+

Rust调用JS函数

+

###1 指定JS对象

+

在Rust中调用JS函数,需要进行指定JS对象,也就是说,得明确告诉Rust,这个JS函数是从JS哪儿拿来的用的。

+

主要在于下面两个方式:

+
    +
  • js_namespace: 是一个可选的属性,用于指定一个JavaScript命名空间,其中包含将要在wasm模块中导出的函数。如果没有指定js_namespace,则所有的导出函数将被放置在全局命名空间下。
  • +
  • js_name: 是另一个可选属性,它用于指定JavaScript中的函数名称。如果没有指定js_name,则导出函数的名称将与Rust中的函数名称相同。
  • +
+

###2 JS原生函数

+

对于一些JS原生函数,在Rust中,需要去寻找替代方案,比如我们上一篇文章中讲的console.log()函数,是不是觉得好麻烦啊!

+

那么,你想直接在Rust中调用JS原生函数吗?!

+

此处,就以console.log()函数为例,直接在Rust中引入并调用,免去替代方案的烦恼。

+

首先,给出Rust代码:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
#[wasm_bindgen]
+extern "C" {
+    #[wasm_bindgen(js_namespace = console)]
+    fn log(message: &str);
+}
+
+#[wasm_bindgen]
+pub fn call_js_func() {
+    log("hello, javascript!");
+}
+
+
+

如上代码中,call_js_func函数,顾名思义,此处是调用了js函数,并传入参数hello, javascript!

+

那么,call_js_func函数上方的代码,我们来一步步解析一下:

+
    +
  1. 第一行代码#[wasm_bindgen]是Rust的属性,它告诉编译器将函数导出为WebAssembly模块
  2. +
  3. extern "C"是C语言调用约定,它告诉Rust编译器将函数导出为C语言函数
  4. +
  5. #[wasm_bindgen(js_namespace = console)]告诉编译器将函数绑定到JavaScript中的console对象
  6. +
  7. fn log(message: &str)是一个Rust函数,它接受一个字符串参数,并将其打印到JavaScript中的console对象中
  8. +
+

此处与JS交互的关键是js_namespace。在Rust中,js_namespace是用于指定JavaScript命名空间的属性。在WebAssembly中,我们可以通过它将函数绑定到JavaScript中的对象上。

+

在上述代码中,#[wasm_bindgen(js_namespace = console)]告诉编译器将函数绑定到JavaScript中的console对象。这意味着在JS中使用console.log()函数来调用Rust中的log()函数。

+

因此,类似的原生函数,都可以使用该方法来实现调用。

+

最后,我们在JS中调用下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
<script type="module">
+    import init, { call_js_func } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+        call_js_func();
+    }
+
+    run();
+</script>
+
+
+

可以在浏览器的控制台中看到hello, javascript!。妙啊!

+

其实对于console.log()而言,还有另一种调用方式,那就是使用js_namespacejs_name同时指定。

+

或许,你会问,这有什么不同吗?是的,这有些不同。

+

不知道你是否发现,当前这个案例中,指定了js_namespaceconsole,但是真实执行的函数是log(),那么这个log函数的指定,其实是体现在Rust中同样的函数名log。也就是说,该案例的log()就是console.log()中的log()

+

我们来换个名字看看,将原来的log()换成log2()

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+
+
#[wasm_bindgen]
+extern "C" {
+    #[wasm_bindgen(js_namespace = console)]
+    fn log2(message: &str);
+}
+
+#[wasm_bindgen]
+pub fn call_js_func() {
+    log2("hello, javascript!")
+}
+
+
+

然后编译后去控制台看看,就会看到报错:

+
+ +
+
1
+
+
TypeError: console.log2 is not a function
+
+
+

因此,当我们使用js_namespacejs_name结合的方式,在此处是可以进行自定义函数名的。

+

看一下Rust代码:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
#[wasm_bindgen]
+extern "C" {
+    #[wasm_bindgen(js_namespace = console)]
+    fn log(message: &str);
+
+    #[wasm_bindgen(js_namespace = console, js_name = log)]
+    fn log_str(message: &str);
+}
+
+#[wasm_bindgen]
+pub fn call_js_func() {
+    log_str("hello, javascript!")
+}
+
+
+

此处,重新定义了一个函数log_str,但是其指定了js_namespace = consolejs_name = log,那么此处,就可以使用自定义的函数名。

+

直接编译后,在控制台看一下,可以直接看到正常输出:hello, javascript!

+

总结一下,如果没有指定js_name,则 Rust 函数名称将用作 JS 函数名称。

+

###3 自定义JS函数

+

在一定场景下,需要使用Rust调用JS函数,比如对于一些对于JS而言更有优势的场景——用JS操作DOM,用Rust计算。

+

首先,创建一个文件index.js,写入一个函数:

+
+ +
+
1
+2
+3
+
+
export function addIt(m, n) {
+    return m + n;
+};
+
+
+

当前的文件结构关系如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+
.
+├── Cargo.lock
+├── Cargo.toml
+├── README.md
+├── index.html
+├── index.js
+├── pkg
+│   ├── README.md
+│   ├── hello_wasm.d.ts
+│   ├── hello_wasm.js
+│   ├── hello_wasm_bg.wasm
+│   ├── hello_wasm_bg.wasm.d.ts
+│   └── package.json
+├── src
+│   └── lib.rs
+└── target
+    ├── CACHEDIR.TAG
+    ├── debug
+    ├── release
+    └── wasm32-unknown-unknown
+
+
+

其中,index.jslib.rs,以及hello_wasm_bg.wasm都是不在同一级别的,index.js都在其它两个文件的上一级。记住这个机构关系!

+

然后,在lib.rs中,指定函数:

+
+ +
+
1
+2
+3
+4
+
+
##./index.js")]
+extern "C" {
+    fn addIt(m: i32, n: i32) -> i32;
+}
+
+
+

其中,raw_module = "../index.js"的意思是,指定对应的index.js文件,大家应该清楚,此处指定的是刚刚创建的index.jsraw_module的作用就是用来指定js文件的。

+

这段代码在前端,可以等同于:

+
+ +
+
1
+
+
import { addIt } from '../index.js'
+
+
+

这样在前端都不用引入了,直接在Rust中引入了,感觉还有点奇妙的。

+

接着,在Rust调用该函数:

+
+ +
+
1
+2
+3
+4
+
+
#[wasm_bindgen]
+pub fn call_js_func() -> i32 {
+    addIt(1, 2)
+}
+
+
+

最后,在前端调用,编译后,在浏览器的控制台中可以看到输出结果了!

+

总结一下,这里有几个注意点:

+
    +
  1. JS的函数必须要export,否则将无法调用;
  2. +
  3. raw_module只能用来指定相对路径,并且,大家可以在浏览器的控制台中注意到,此处的../的相对路径,其实是以wasm文件而言的相对路径,这里一定要注意呀!
  4. +
+

JS调用Rust的struct

+

现在,来点炸裂的,JS调用Rust的struct?!

+

JS中连struct都没有,这玩意儿导出来会是什么样,得怎么在JS中调用呢?!

+

首先,定义一个struct,并且声明几个方法:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
#[wasm_bindgen]
+pub struct User {
+    name: String,
+    age: u32
+}
+
+#[wasm_bindgen]
+impl User {
+    #[wasm_bindgen(constructor)]
+    pub fn new(name: String, age: u32) -> User {
+        User { name, age }
+    }
+
+    pub fn print_user(&self) {
+        log(format!("name is : {}, age is : {}", self.name, self.age).as_str());
+    }
+
+    pub fn set_age(&mut self, age: u32) {
+        self.age = age;
+    }
+}
+
+
+

此处,声明了一个struct名为User,包含nameage两个字段,并声明了newprint_userset_age方法。

+

其中还有一个未见过的#[wasm_bindgen(constructor)]constructor用于指示被绑定的函数实际上应该转换为调用 JavaScript 中的 new 运算符。或许你还不太清晰,继续看下去,你就会明白了。

+

接着,在JS中调用这个struct,和其方法:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+
+
<script type="module">
+    function addIt2(m, n) {
+        return m + n;
+    };
+
+    import init, { User } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+
+        const user = new User('kuari', 20);
+        user.set_age(21);
+        user.print_user();
+    }
+
+    run();
+</script>
+
+
+

可以看到,这里的用法就很熟悉了!

+

大概想一下,在Rust中要如何调用?也就是直接new一个——User::new('kuari', 20)

+

此处在JS中,也是如此,先new一个!

+

然后很自然地调用struct的方法。

+

编译后,打开浏览器,可以在控制台看到输出:name is : kuari, age is : 21

+

其实,或许大家会很好奇,起码我是非常好奇的,Rust的struct在JS中到底是一个怎样的存在呢?

+

这里直接添加一个console.log(user),就可以在输出看到。那么到底在JS中是一个怎样的存在呢?请各位动手打印一下看看吧!:P

+

总结

+

本篇文章中,主要讲述了Rust与JS的交互,体现在Rust与JS的相互调用,这是建立在上一篇文章中类型转换的基础上的。

+

Rust与JS的函数相互调用的学习成本还是较大的,而且对比Go写wasm,Rust的颗粒度是非常细的,几乎可以说是随心所欲了。

+

比较炸裂的就是Rust的struct导出给JS用,这对于Rust与JS的交互而言,还是非常棒的体验。

+ +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..7aa0993 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..94b09fa Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" new file mode 100644 index 0000000..5606cc4 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" new file mode 100644 index 0000000..e1bd93d Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/index.html" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/index.html" new file mode 100644 index 0000000..4221d84 --- /dev/null +++ "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\344\272\214dom\345\222\214\347\261\273\345\236\213\350\275\254\346\215\242/index.html" @@ -0,0 +1,1581 @@ + + + + +使用Rust和WebAssembly整花活儿(二)——DOM和类型转换 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换 + + +
+ + +
+ + +
+

+ 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换 +

+ + +

+ WebAssembly可以使用多种语言开发, 如果你更注重性能和安全性, 那么使用Rust整花活儿是个更好的选择~ +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

在上一篇文章《使用Rust和WebAssembly整花活儿(一)——快速开始》中,描述了如何创建项目和快速生成wasm并在前端中使用,迈出了整花活儿的第一步。

+

在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。

+

本篇文章中,将基于上一篇文章中创建的项目来继续开发。

+

源码:github.com/Kuari/hello-wasm

+

环境

+
    +
  • Rust 1.70.0
  • +
  • wasm-bindgen 0.2.87
  • +
  • web-sys 0.3.64
  • +
+

DOM

+

配置依赖

+

要操作DOM,需要引入新的依赖web-sys,因此,可以配置Cargo.toml中依赖如下:

+
+ +
+
1
+2
+3
+
+
[dependencies]
+wasm-bindgen = "0.2.87"
+web-sys = { version = "0.3.64", features = [] }
+
+
+

你或许会好奇,这个features是什么,讲真,我一开始很好奇,又没看到什么特别的说明,试错才发现,原来是要手动引入功能依赖…比如说,当你需要在Rust中使用JS的console,那么你需要在features中加入console

+

获取Document

+

在Rust中使用Document,我们需要按照上一步的说明,添加features。那么这里有一个依赖关系,首先在Rust中获取window,然后再获取document

+

因此,添加features后如下:

+
+ +
+
1
+2
+3
+
+
[dependencies]
+wasm-bindgen = "0.2.87"
+web-sys = { version = "0.3.64", features = ["Window", "Document"] }
+
+
+

然后在lib.rs中创建一个函数,用来调用document

+
+ +
+
1
+2
+3
+4
+5
+
+
#[wasm_bindgen]
+pub fn update_message() {
+    let window = web_sys::window().expect("Failed to load window");
+    let document = window.document().expect("Failed to load document");
+}
+
+
+

那么,现在就是在Rust中解锁了document,就可以在前端为所欲为了!

+

操作Element

+

那么开始操作一波,首先得获取到Element……

+

是的,你没有想错,继续来添加features吧,此处要添加一个Element

+
+ +
+
1
+2
+3
+
+
[dependencies]
+wasm-bindgen = "0.2.87"
+web-sys = { version = "0.3.64", features = ["Window", "Document", "Element"] }
+
+
+

ok,那么继续。此处设定函数传入两个参数selectormessage,然后通过selector获取element,更新值为message参数的值。完整函数如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
#[wasm_bindgen]
+pub fn update_message(selector: &str, message: &str) {
+    let window = web_sys::window().expect("Failed to load window");
+    let document = window.document().expect("Failed to load document");
+    let element = document.query_selector(selector).expect("Failed to load element");
+
+    if let Some(element) = element {
+        element.set_inner_html(message);
+    } else {
+        panic!("Failed to set inner html")
+    }
+}
+
+
+

编译

+

将写完的Rust项目编译成wasm:

+
+ +
+
1
+
+
wasm-pack build --target web
+
+
+

在html中调用

+

基于上一篇文章的项目中的html,此处添加一个div,id为message,添加调用wasm的update_message函数,代码如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
<body>
+    <div id="message"></div>
+</body>
+
+<script type="module">
+    import init, { update_message } from './pkg/hello_wasm.js'; // 引入update_message函数
+
+    const run = async () => {
+        await init();
+
+        update_message('#message', '<h1>Hello, Rust!</h1>'); // 调用update_message函数
+    }
+
+    run();
+</script>
+
+
+

在浏览器验证

+

启动一个http server,然后在浏览器查看,可以看到在页面上出现一个h1标签的Hello, Rust!

+

发现更多方法

+

按照文章来写的过程中,你应该会发现一个问题——怎么这些方法没有补全?!

+

是的,没错的,(至少我发现)当前web-sys并没有补全,所以只能结合开发者优秀的前端技能和丰富的官方文档来开发了。

+

Rust与JS的类型相互转换

+

对于wasm而言,性能固然是提升的,但是类型转换一直是个问题。当大量数据需要在wasm/js中进行类型转换时,这对性能来说,真的是个灾难。之前在使用go开发wasm时,就遇到过这样的问题,需要用官方的方法来进行手动类型转换,然而wasm处理的是一个很大的数据量……

+

不过好在Rust的类型支持真的挺丰富的!

+

基础类型

+

基础类型挺简单的,而且Rust的范性也很好地支持了很多类型。如下是基础类型映射表:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Rust类型JavaScript类型
i8number
i16number
i32number
i64BigInt
u8number
u16number
u32number
u64BigInt
f32number
f64number
boolboolean
charstring
&strstring
Stringstring
&[T] 例如:&[u8][T] 例如:Uint8Array
VecArray
+

基础类型转换示例

+

lib.rs文件中,创建一个函数,挑选几个类型作为参数传入,然后将其读取并打印:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
#[wasm_bindgen]
+pub fn print_values(js_number: i32, js_boolean: bool, js_uint8_array: &[u8], js_number_array: Vec<i32>) {
+    println!("js number: {}", js_number);
+    println!("js boolean: {}", js_boolean);
+
+    for item in js_uint8_array {
+        println!("js Uint8Array item: {}", item);
+    }
+
+    for item in js_number_array {
+        println!("js number array item: {}", item);
+    }
+}
+
+
+

可以看到该函数传入了JS的numberbooleanUint8ArrayArray四个类型的参数。

+

然后编译:

+
+ +
+
1
+
+
wasm-pack build --target web
+
+
+

接着,在前端中引入函数并调用:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
<script type="module">
+    import init, { print_values } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+
+        const jsNumber = 10;
+        const jsBoolean = true;
+
+        const jsUint8Array = new Uint8Array(3);
+        jsUint8Array[0] = 1;
+        jsUint8Array[1] = 2;
+        jsUint8Array[2] = 3;
+
+        const jsNumberArray = [30, 40, 50];
+
+        print_values(jsNumber, jsBoolean, jsUint8Array, jsNumberArray);
+    }
+
+    run();
+</script>
+
+
+

最后,启动http server并打开浏览器,在控制台可以看到…看不到?!

+

是的,没错,Rust的println!只会将打印的内容发送到Rust的标准输出流,而不是前端的控制台。如果想在控制台中打印,那么需要调用JS的console了。

+

使用新的功能,第一步就是添加featuresCargo.toml中添加console如下:

+
+ +
+
1
+2
+3
+
+
[dependencies]
+wasm-bindgen = "0.2.87"
+web-sys = { version = "0.3.64", features = ["Window", "Document", "Element", "console"] }
+
+
+

在Rust中调用console.log()如下:

+
+ +
+
1
+
+
web_sys::console::log_1(&"Hello, Rust!".into());
+
+
+

此处将其封装成一个函数:

+
+ +
+
1
+2
+3
+
+
fn console_log(message: String) {
+    web_sys::console::log_1(&message.into());
+}
+
+
+

然后,将示例函数的println改成console_log()format!,函数代码如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
#[wasm_bindgen]
+pub fn print_values(js_number: i32, js_boolean: bool, js_uint8_array: &[u8], js_number_array: Vec<i32>) {
+    console_log(format!("js number: {}", js_number));
+    console_log(format!("js boolean: {}", js_boolean));
+
+    for item in js_uint8_array {
+        console_log(format!("js Uint8Array item: {}", item));
+    }
+
+    for item in js_number_array {
+        console_log(format!("js number array item: {}", item));
+    }
+}
+
+
+

最后,编译之后,打开浏览器,就可以在控制台看到输出:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
js number: 10
+js boolean: true
+js Uint8Array item: 1
+js Uint8Array item: 2
+js Uint8Array item: 3
+js number array item: 30
+js number array item: 40
+js number array item: 50
+
+
+

通用类型

+

Rust中提供了一个通用的类型——JsValue,可以作为任何JS类型。

+

这里给一个简单的案例,设置一个函数,使用JsValue作为参数传入,并打印。

+

创建函数:

+
+ +
+
1
+2
+3
+4
+
+
#[wasm_bindgen]
+pub fn print_js_value(val: JsValue) {
+    console_log(format!("{:?}", val));
+}
+
+
+

然后编译成wasm文件。

+

在html中调用:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
<script type="module">
+    import init, { print_js_value } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+
+        const jsNumber = 10;
+        const jsBoolean = true;
+
+        const jsUint8Array = new Uint8Array(3);
+        jsUint8Array[0] = 1;
+        jsUint8Array[1] = 2;
+        jsUint8Array[2] = 3;
+
+        const jsNumberArray = [30, 40, 50];
+
+        print_js_value(jsNumber);
+        print_js_value(jsBoolean);
+        print_js_value(jsUint8Array);
+        print_js_value(jsNumberArray);
+    }
+
+    run();
+</script>
+
+
+

在html中,传入了不同类型的参数,但是在浏览器的控制台中可以看到,将所有不同类型的参数都打印出来了:

+
+ +
+
1
+2
+3
+4
+
+
JsValue(10)
+JsValue(true)
+JsValue(Uint8Array)
+JsValue([30, 40, 50])
+
+
+

Result

+

Result在Rust中是一个很重要的存在,经常写Rust的话,也不想在写WebAssembly时改变开发习惯。

+

其实对于JS而言,Result可以直接在catch中捕获到,只是说,这里我们需要定义好参数类型。

+

####1 使用Result返回报错

+

首先来一个只返回报错的场景:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
#[wasm_bindgen]
+pub fn only_return_error_when_result(count: i32) -> Result<(), JsError> {
+    if count > 10 {
+        Ok(())
+    } else {
+        Err(JsError::new("count < 10"))
+    }
+}
+
+
+

这里返回类型是Result,但是仅仅返回了一个错误。值得注意的是,这里的报错使用的类型是JsError,当然,这里也可以使用JsValue

+

然后在html调用:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
+
<script type="module">
+    import init, { only_return_error_when_result } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+
+        try {
+            only_return_error_when_result(1);
+            console.log('1 is ok');
+        } catch(error) {
+            console.log('An error is reported when the input parameter is 1: ', error);
+        }
+
+        try {
+            only_return_error_when_result(100);
+            console.log('100 is ok');
+        } catch(error) {
+            console.log('An error is reported when the input parameter is 100: ', error);
+        }
+    }
+
+    run();
+</script>
+
+
+

这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了catch来捕获错误。

+

那么,在浏览器的控制台可以看到输出:

+
+ +
+
1
+2
+
+
An error is reported when the input parameter is 1:  Error: count < 10
+100 is ok
+
+
+

####2 使用Result返回正常值和错误

+

那么,如果想既返回正常值,也想返回错误呢?Rust返回一个Result是没有问题,那么JS怎么解析呢?

+

直接上Rust代码:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
#[wasm_bindgen]
+pub fn return_all_when_result(count: i32) -> Result<i32, JsError> {
+    if count > 10 {
+        Ok(count + 10)
+    } else {
+        Err(JsError::new("count < 10"))
+    }
+}
+
+
+

该函数,获取到参数后,如果满足条件,加10后返回,否则报错。

+

那么看看html中如何调用:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
+
<script type="module">
+    import init, { return_all_when_result } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+
+        try {
+            const res = return_all_when_result(1);
+            console.log(`get ${res}`);
+        } catch(error) {
+            console.log('An error is reported when the input parameter is 1: ', error);
+        }
+
+        try {
+            const res = return_all_when_result(100);
+            console.log(`get ${res}`);
+        } catch(error) {
+            console.log('An error is reported when the input parameter is 100: ', error);
+        }
+    }
+
+    run();
+</script>
+
+
+

是的,没错,正常获取就行了……/捂脸哭

+

这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了catch来捕获错误。

+

最后,就是在浏览器的控制台中看到:

+
+ +
+
1
+2
+
+
An error is reported when the input parameter is 1:  Error: count < 10
+get 110                                                             
+
+
+

直接引入JS类型

+

如果你想更直接一点,那么可以直接引入JS类型!这里主要是利用js-sys这个依赖,可以在官方文档上看到很多JS的类型和函数,直接引入即可使用。当然,一定场景下,直接引入的类型,是需要手动转换类型的。

+

####1 配置依赖

+

Cargo.toml中添加js-sys依赖:

+
+ +
+
1
+2
+3
+4
+
+
[dependencies]
+wasm-bindgen = "0.2.87"
+web-sys = { version = "0.3.64", features = ["Window", "Document", "Element", "console"] }
+js-sys = "0.3.61"
+
+
+

####2 Uint8Array

+

首先以Uint8Array举例,在lib.rs头部引入类型:

+
+ +
+
1
+
+
use js_sys::Uint8Array;
+
+
+

然后创建一个函数,参数和返回都是Uint8Array类型:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+
+
#[wasm_bindgen]
+pub fn print_uint8_array(js_arr: Uint8Array) -> Uint8Array {
+    // new Uint8Array
+    let mut arr = Uint8Array::new_with_length(3);
+
+    // Uint8Array -> vec
+    for (index, item) in js_arr.to_vec().iter().enumerate() {
+        console_log(format!("{} - the item in js_arr: {}", index, item));
+    }
+
+    // Avoid type conversion
+    // Use the method of the type itself
+    for index in 0..js_arr.length() {
+        console_log(format!("{} - the item in js_arr: {}", index, js_arr.get_index(index)));
+    }
+
+    // vec -> Uint8Array
+    let vec = vec![1, 2, 3];
+    let arr2 = Uint8Array::from(vec.as_slice());
+    arr = arr2.clone();
+
+    // Use the method of the type itself
+    arr.set_index(0, 100);
+
+    arr
+}
+
+
+
+

忽略该函数中无意义的逻辑和arr变量的警告,只是为了演示用法。

+
+

可以在代码中看到,直接引入的Uint8Array有自己的方法,一定场景下,需要转换类型,但是最好避免进行类型转换,而直接使用其自带的方法。

+

这里可以简要总结下,就是最好一定场景内全部使用直接引入的JS类型,或者直接全部使用Rust类型来代替JS类型,两者都存在场景下,手动转换类型是件很糟糕的事。

+

####3 Date

+

Date类型,在上面的篇章中都没有提及,这里可以直接引入JS的Date类型来使用。

+

首先是引入类型:

+
+ +
+
1
+
+
use js_sys::Date;
+
+
+

然后,创建一个函数,返回时间戳:

+
+ +
+
1
+2
+3
+4
+5
+
+
#[wasm_bindgen]
+pub fn return_time() -> f64 {
+    let date = Date::new_0();
+    date.get_time()
+}
+
+
+

接着,在html中调用:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
<script type="module">
+    import init, { return_time } from './pkg/hello_wasm.js';
+
+    const run = async () => {
+        await init();
+
+        console.log('current time: ', return_time());
+    }
+
+    run();
+</script>
+
+
+

最后,在浏览器的控制台中,可以看到:

+
+ +
+
1
+
+
current time:  1686979932833
+
+
+

总结

+

本文中,主要讲述了如何使用Rust来实现DOM操作,读者可以根据方法自己去找到合适的方法,来实现自己的场景。其次,还讲述了Rust与JS的类型转换,从基础的各自类型的映射,到Rust独有的Result,到直接引入JS类型。当然这里需要注意的是,直接引入JS类型和Rust的基础类型映射JS类型这两种方法尽量不要混用,混用会导致需要手动类型转换,造成性能损耗。

+

至此,又向Rust和WebAssembly整花活儿迈进了一步~😼

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.2e8b11dfa976d41e74c2f13eba5ef63c.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..7aa0993 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.2e8b11dfa976d41e74c2f13eba5ef63c_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_250x150_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.jpg" new file mode 100644 index 0000000..2741717 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..94b09fa Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" new file mode 100644 index 0000000..5606cc4 Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_1600x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" new file mode 100644 index 0000000..e1bd93d Binary files /dev/null and "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_800x0_resize_q75_box.jpg" differ diff --git "a/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/index.html" "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/index.html" new file mode 100644 index 0000000..2b722d7 --- /dev/null +++ "b/p/\344\275\277\347\224\250rust\345\222\214webassembly\346\225\264\350\212\261\346\264\273\345\204\277\345\233\233\346\233\264\345\260\217\346\233\264\345\260\217\347\232\204wasm\346\226\207\344\273\266\344\275\223\347\247\257/index.html" @@ -0,0 +1,1001 @@ + + + + +使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积 + + +
+ + +
+ + +
+

+ 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积 +

+ + +

+ 叠加所有优化buff,最终能够减少约77%的体积,很强! +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

在上一篇文章《使用Rust和WebAssembly整花活儿(三)——Rust与JS交互》中,讲述了Rust与JS的交互,包括Rust与JS的函数相互调用,比较炸裂的功能就是使用JS调用Rust的struct,JS本身连struct都没有,居然可以调用Rust的struct,这对于Rust开发者的开发体验而言,是真的很棒!

+

基于前面的系列文章,已经足以使用Rust开发一个完整的功能了。

+

但是,在前端引入wasm文件时,还是可能存在一些问题,比如wasm文件较大,导致网页访问时间较长,用户体验较差。本篇文章将会通过多种途径来减少Rust编译wasm文件的体积,以减少前端加载wasm文件的时间。

+

曾经一段时间,我一直用Go开发WebAssembly,其编译后的wasm文件体积还是较大的,为了减少wasm文件的体积真是煞费苦心,1xMB大小的wasm文件真的是太痛了……但是体积优化往往都会指向一条“充满魅惑的不归路”——换成Rust开发😆!如果你已经在用Rust开发WebAssembly了,那么恭喜你,对Go而言,在wasm体积上,你已经赢在起跑线上了。

+

环境

+
    +
  • Rust 1.70.0
  • +
  • wasm-bindgen 0.2.87
  • +
+

查看体积

+

在真正去减少体积前,我们需要来先看一下,当前情况下体积是多少,方便后续对比前后体积。

+

查看体积的方式有多种,这里推荐几个,(Linux和MacOS)使用其一即可。

+

ls

+

可以使用ls -l或者ll

+
+ +
+
1
+2
+
+
$ ll pkg/hello_wasm_bg.wasm
+-rw-r--r--  1 kuari  staff    23K Jul 20 21:52 pkg/hello_wasm_bg.wasm
+
+
+

stat

+
+ +
+
1
+2
+
+
$ stat pkg/hello_wasm_bg.wasm
+16777222 142141572 -rw-r--r-- 1 kuari staff 0 23347 "Jul 20 21:52:53 2023" "Jul 20 21:52:01 2023" "Jul 20 21:52:01 2023" "Jul 20 21:52:01 2023" 4096 48 0 pkg/hello_wasm_bg.wasm
+
+
+

wc

+
+ +
+
1
+2
+
+
$ wc -c pkg/hello_wasm_bg.wasm
+   23347 pkg/hello_wasm_bg.wasm
+
+
+

wc为例,当前该wasm文件体积为23347b

+

代码层面

+
+

Link-Time Optimization (LTO) 是指在程序链接时进行的一种过程间优化(interprocedural optimization)。它允许编译器在链接阶段对多个编译单元进行优化,从而提高程序的性能、可靠性和安全性。

+
+

从代码层面优化,主要是利用LTO(Link-Time Optimization)。

+

代码内

+

Cargo.toml中开启LTO:

+
+ +
+
1
+2
+
+
[profile.release]
+lto = true
+
+
+

开启LTO虽然能够减少编译后的体积,但是也会增加编译时间。

+

LTO开启后,默认是在减少一定程度的编译体积的情况下,要确保编译的时间。如果你的需求就是更小的体积,而不是较短的时间,那么,可以通过手动指定编译等级来让LTO作出改变。

+

在代码内可以使用如下等级:

+
    +
  • s:默认的 LTO 等级。它会进行最基本的 LTO 优化,包括内联函数、函数重写、数据重排等
  • +
  • z:最高级的 LTO 等级。它会进行更复杂的 LTO 优化,包括死代码消除、内存分配优化、安全性优化等
  • +
+

那么可以在Cargo.toml中这么配置:

+
+ +
+
1
+2
+3
+
+
[profile.release]
+lto = true
+opt-level = 'z'
+
+
+

原始的文件体积是23347b,现在编译后看一下体积:

+
+ +
+
1
+2
+
+
$ wc -c pkg/hello_wasm_bg.wasm
+   19879 pkg/hello_wasm_bg.wasm
+
+
+

很明显是减少体积!但是,使用z等级并不代表一定每次体积都会比s小的,有时候s也会比z小,这需要视代码情况而定。

+

代码外

+

在代码外,可以使用wasm-opt来进行优化,其可以对 WebAssembly 模块进行多方面的优化,当然本篇文章中重点在体积方面(挖个坑,后面再详聊/狗头)。并且wasm-opt可以对所有符合WebAssembly规范的wasm文件进行优化,所以,就算你不是Rust写的,那也可以用其进行优化。(想想我曾经Go写的wasm,也是有多一个法子可以优化一把了……)

+

首先,来看一下wasm-opt的基本优化参数:

+
    +
  • -o:指定优化后的模块输出文件
  • +
  • -O:启用默认优化,等同于-Os参数
  • +
  • -O0:不进行任何优化
  • +
  • -O1:进行一些基本的优化,例如内联函数优化和死代码消除优化
  • +
  • -O2:进行更为彻底的优化,例如函数重写、数据重排、内存分配优化等
  • +
  • -O3:进行最为彻底的优化,包括一些可能影响程序功能的优化
  • +
  • -O4:与 -O3 相同,但会启用更为激进的优化
  • +
  • -Os:优化目标是减小代码大小,会进行一些可能影响性能的优化
  • +
  • -Oz:与 -Os 相同,但会启用更为激进的优化
  • +
+

基于本篇文章主题,此处将使用-Os-Oz两种参数,其于上述"代码内"的等级是对应的。

+

此处以原始wasm文件,以-Oz参数来执行一下,看一下对比效果:

+
+ +
+
1
+2
+
+
$ wc -c pkg/output.wasm
+   23194 pkg/output.wasm
+
+
+

再以上述开启“代码内“LTO编译后的wasm文件,以wasm-opt执行一下,看一下对比效果:

+
+ +
+
1
+2
+
+
$ wc -c pkg/output.wasm
+   19871 pkg/output.wasm
+
+
+

总体而言,wasm文件的体积越来越小。只是当前我这里的案例,是沿用系列文章内容的代码,没有什么实际性复杂代码,再者本身体积已经很小了,所以不会特别有效果。

+

网络层面

+

网络层面的话,就是众所周知的在网络传输时,客户端和服务端约定相同的压缩算法,然后服务端给出时进行压缩,客户端接收时进行解压。网络层面可以对传输报文进行压缩,但不丢失信息。

+

比如大家都很熟悉的gzip压缩算法,不过,压缩算法有好几种:

+
    +
  • gzip
  • +
  • compress
  • +
  • deflate
  • +
  • br
  • +
+

其中gzip也是压缩率最高的了,此处就以gzip为例。

+

在网络层面,将wasm文件以gzip压缩,减少其在传输时的体积。虽然减少了传输时的体积,但是浏览器在拿到压缩后的数据,需要消耗一定性能来解密。

+

开启GZIP

+

开启GZIP其实简单,只要前后端约定好都用gzip就行了。

+

首先,前端请求wasm文件时,需要在request header中放入浏览器支持的压缩模式:

+
+ +
+
1
+
+
Accept-Encoding: gzip, deflate
+
+
+

接着,服务端收到这个请求后就可以给出服务端也支持的压缩模式,并告诉浏览器服务端将会用什么压缩模式。

+

跟浏览器通信的方式就是将信息塞到respone header里面:

+
+ +
+
1
+
+
Content-Encoding: gzip
+
+
+

这样就开启GZIP了。

+

然后,就是浏览器接收到response的body和header,知道后端使用gzip压缩的,那么浏览器就会自动用gzip来解压,拿到完整的数据了。

+

服务端支持

+

或许你会想问,浏览器能自动解密,那服务端怎么自动加密呢?要后端写代码让文件加密吗?

+

那当然不是了,直接让http server来完成这个操作。此处以耳熟能详的Nginx为例。

+

最简单的就是一行配置开启gzip了:

+
+ +
+
1
+
+
gzip on;
+
+
+

也可以指定gzip的一些参数,比如可以加密的类型、最小加密长度等等:

+
+ +
+
1
+2
+3
+4
+5
+
+
gzip on;
+gzip_types      text/plain application/xml;
+gzip_proxied    no-cache no-store private expired auth;
+gzip_min_length 1000;
+...
+
+
+

更多的http server配置,可以去各自官方文档查阅。

+

物理层面

+

你可能会惊奇,什么物理层面?!

+

没错,真就是物理层面——直接对wasm文件进行gzip物理压缩!哈哈,这个方法也真是绝了,我之前在Go开发wasm时,寻找减少体积的时候发现的,如果你的wasm已经优化得穷途末路了,不妨大胆试试这个方案。😆

+

还记得上面章节“网络层面“中,有个问题就是是否需要手动压缩,那么这里就是全程手动压缩和解压缩了,哈哈。

+

物理压缩

+

首先,是对wasm文件进行物理层面的gzip压缩,此处先使用原始的wasm(23347b):

+
+ +
+
1
+
+
gzip -c pkg/hello_wasm_bg.wasm > pkg/output.gz
+
+
+

然后,看一下其体积:

+
+ +
+
1
+2
+
+
$ wc -c pkg/output.gz
+   10403 pkg/output.gz
+
+
+

效果卓群,从23347b减少到了10403b!

+

然后来把上述“代码层面”的优化来一遍,看一下最后的体积:

+
+ +
+
1
+2
+
+
$ wc -c pkg/output.gz
+    9237 pkg/output.gz
+
+
+

效果更加卓群了,从19871b减少到了9237b了!

+

所以,此处就是对wasm文件进行物理压缩并存储,然后浏览器请求时,直接请求到.gz文件。

+

物理解压

+

浏览器拿到.gz文件后,需要物理解压。

+

这里推荐使用pako这个前端库,对.gz文件进行解压:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
async function gunzipWasm() {
+  const res = await fetch("target.gz")
+  let buffer = await pako.ungzip(await res.arrayBuffer())
+  // A fetched response might be decompressed twice on Firefox.
+  // See https://bugzilla.mozilla.org/show_bug.cgi?id=610679
+  if (buffer[0] === 0x1F && buffer[1] === 0x8B)
+  {buffer = pako.ungzip(buffer)}
+  return buffer
+}
+
+
+

之后就可以直接使用了。

+

BUFF叠加

+

此处直接将上述所有方法都用起来,直接叠加buff,来看看当前(本系列文章积累的)这个案例能减少多少体积。在“物理层面”章节中,已经累加除了“网络层面”的buff了,所以可以直接使用其结果。而“网络层面”章节中,以gzip来压缩,将gzip的压缩率以40%来估算。

+

那么最终该案例的wasm体积将在5542b,压缩率大约在77%!

+

当然,还要算上一个初始的语言buff——Rust,使用Rust本身就已经导致wasm文件体积很小了。

+

总结

+

本片文章中,从代码层面、网络层面、物理层面共三个层面介绍了对wasm文件的体积优化方案,其中共有四个方案。

+

最后,当前(本系列文章积累的)该案例叠加了所有buff之后,能够减少77%的体积,真的感觉挺棒的了,哈哈。

+

希望能够对各位有所帮助。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\345\211\215\347\253\257\346\226\207\344\273\266\350\212\261\345\274\217\347\233\264\344\274\240oss\345\220\216\347\253\257\351\202\243\346\210\221\350\265\260/index.html" "b/p/\345\211\215\347\253\257\346\226\207\344\273\266\350\212\261\345\274\217\347\233\264\344\274\240oss\345\220\216\347\253\257\351\202\243\346\210\221\350\265\260/index.html" new file mode 100644 index 0000000..8210b42 --- /dev/null +++ "b/p/\345\211\215\347\253\257\346\226\207\344\273\266\350\212\261\345\274\217\347\233\264\344\274\240oss\345\220\216\347\253\257\351\202\243\346\210\221\350\265\260/index.html" @@ -0,0 +1,810 @@ + + + + +前端文件花式直传OSS!后端:那我走? + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ 前端文件花式直传OSS!后端:那我走? +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简介

+

前端还在传文件给后端吗?你们的服务器扛得住吗?什么……老板砸钱加机器?!告辞!/狗头

+

前后端文件传输涉及数据较大,往往会成为很多项目的性能瓶颈。常见的传输方式也有不少,相对来说,OSS直传能够减轻很大压力。

+

本文我们来列举下常见的和oss直传的几种传输方式,并列举其优劣。

+

常见方式

+

表单上传

+

表单上传文件是最常见的方式,前后端开发小伙伴都很轻松,前端哐哐传,后端哐哐收就成了。其过程如下图所示。

+

form上传

+

优势:

+
    +
  • 简单方便,开发量小
  • +
  • 前后端原生支持,无需额外第三方库支持
  • +
+

Base64上传

+

Base64方式上传文件,多常见于小文件,如小图片等,前后端都可直接使用String类型发送和接收。不过在前端,需要将文件转成base64数据,不仅会增加些性能消耗,还会增加传输数据的体积。而对于后端,如果并不是想直接存储base64数据,也还需要将其转成文件再存储,也会增加后端的性能消耗。

+

该上传方式可适用于Resetful Api,也可适用于文件的加密、回调接口携带文件等等。其过程如下图所示。

+

base64上传

+

优势:

+
    +
  • 适用于Resetful Api,可用于加密、回调等场景,较为灵活
  • +
+

劣势:

+
    +
  • 前后端文件与base64数据转换需要消耗性能
  • +
  • 只适用于小文件
  • +
+

OSS直传

+

此处的OSS直传方案都是使用的阿里云的OSS产品,以下将介绍三个方案,可适用于不同的场景。

+

Browser.js SDK上传

+

该方案可在前端直接通过browser.js上传文件到OSS,可分成三步:

+
    +
  1. 前端使用SDK直传OSS
  2. +
  3. 前端上传完成后请求后端,通知上传完成
  4. +
  5. 后端检测OSS上该文件是否存在(可选)
  6. +
+

其流程如下图所示。

+

browser直传

+

Browser.js的方式需要前端安装阿里云的库ali-oss,然后在前端调用。直传还需要OSS账户的Key和Secret,因此为了安全考虑,需要建立RAM账户,然后前端向后端先请求一个STS临时访问凭证来完成直传,其流程如下图所示。

+

browser直传2

+

Javascript客户端签名直传

+

Javascript客户端签名直传,需要先从后端获取临时签名,其流程与上一步的browser.js方案大致相同,不同点在于:

+
    +
  • 无需第三方库支持,直接表单上传
  • +
  • 原生支持后端上传回调(下一步骤讲述)
  • +
+

其流程如下图所示。

+

javascript签名直传

+

不过该方案做下来,我感觉最大的问题是权限配置有点麻烦……/泪眼

+

服务端签名直传并设置上传回调

+

该方案其实是上面方案——Javascript客户端签名直传的升级版本,其加上了后端的上传回调功能。不错呦~

+

前端需要改动的很少,只需要在请求参数中加上callback参数即可,该参数为后端加密,在签名请求的响应中一起返回回来,内加密了后端回调接口。在前端直传完成后,后端回调接口将会接收到相关文件参数,包括文件路径、大小、类型等。最后OSS会将回调接口response转发给前端,响应直传OSS的请求。

+

其流程如下图所示。

+

后端签名直传且回调

+

对比

+

传统方式相比直传OSS,相对来说有三个缺点:

+
    +
  • 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • +
  • 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
  • +
  • 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。
  • +
+

当然,对于规模较小、成本较低的项目来说,常见的上传方式还是适合的,毕竟没有最好的,只有最适合的。

+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\345\216\237\346\235\245\346\265\217\350\247\210\345\231\250\345\216\237\347\224\237\346\224\257\346\214\201js\345\244\215\345\210\266\345\210\260\345\211\252\345\210\207\346\235\277/index.html" "b/p/\345\216\237\346\235\245\346\265\217\350\247\210\345\231\250\345\216\237\347\224\237\346\224\257\346\214\201js\345\244\215\345\210\266\345\210\260\345\211\252\345\210\207\346\235\277/index.html" new file mode 100644 index 0000000..aca183e --- /dev/null +++ "b/p/\345\216\237\346\235\245\346\265\217\350\247\210\345\231\250\345\216\237\347\224\237\346\224\257\346\214\201js\345\244\215\345\210\266\345\210\260\345\211\252\345\210\207\346\235\277/index.html" @@ -0,0 +1,685 @@ + + + + +原来浏览器原生支持JS复制到剪切板 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ 原来浏览器原生支持JS复制到剪切板 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

第三方库的痛苦

+

在日常的前端开发中,经常需要将一些数据从网页上复制到剪切板中。而实现复制功能,第一时间想到的就是引入第三方库。

+

曾经过多不少第三方的剪切板的库,是真的很繁琐,又是创建对象,又是绑定DOM,头都要炸了,就个简单的复制功能,第三方库换来换去地测试……

+

后来看到了vueuse可以直接用,突然觉得,哇!真棒!

+

直到有一天,搜到了Clipboard api……

+

原生支持

+

官方文档:https://developer.mozilla.org/en-US/docs/Web/API/Clipboard

+

不管是读,还是写,统统搞定!而且都还是异步方法。

+

比如复制文本到剪切板:

+
+ +
+
1
+
+
navigator.clipboard.writeText("<empty clipboard>")
+
+
+

复制canvas到剪切板:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
function copyCanvasContentsToClipboard(canvas, onDone, onError) {
+  canvas.toBlob((blob) => {
+    let data = [new ClipboardItem({ [blob.type]: blob })];
+
+    navigator.clipboard.write(data).then(
+      () => {
+        onDone();
+      },
+      (err) => {
+        onError(err);
+      }
+    );
+  });
+}
+
+
+

读取剪切板的文本:

+
+ +
+
1
+
+
const txt = navigator.clipboard.readText()
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\345\237\272\344\272\216python3\345\222\214js\347\232\204\345\211\215\345\220\216\347\253\257aes\345\212\240\350\247\243\345\257\206/index.html" "b/p/\345\237\272\344\272\216python3\345\222\214js\347\232\204\345\211\215\345\220\216\347\253\257aes\345\212\240\350\247\243\345\257\206/index.html" new file mode 100644 index 0000000..6b2ccee --- /dev/null +++ "b/p/\345\237\272\344\272\216python3\345\222\214js\347\232\204\345\211\215\345\220\216\347\253\257aes\345\212\240\350\247\243\345\257\206/index.html" @@ -0,0 +1,778 @@ + + + + +基于python3和js的前后端aes加解密 + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ 基于python3和js的前后端aes加解密 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

简述

+

在特定敏感数据的场景需要加密,一开始采用rsa加密,但是rsa加密对性能要求较高,在解密时候对于数据量限制较大,导致加密传输的数据量上限较低。而采用Base64虽然简单明了但是解密过于简单。因此采用折中的对称加密aes

+

aes加密需要前后端加密类型相同,因此此处采用CTR,其对加密文本没有长度限制。

+

前端实现

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
let crypto = require("crypto")
+
+export function aesEncrypted(key, text) {
+  let iv = Buffer.concat([ crypto.randomBytes(12), Buffer.alloc(4, 0) ])
+  let cipher = crypto.createCipheriv("aes-128-ctr", key, iv)
+  return iv.toString('hex') + cipher.update(text, 'utf8', 'hex') + cipher.final('hex')
+}
+
+
+

后端实现

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
def aesDecryption(key_: str, de_text: str) -> str:
+    """
+    aes解密函数
+    :param key_: aes的key
+    :param de_text: aes加密的密文
+    :return: 解密的文本
+    """
+    ct = codecs.decode(de_text.encode(), 'hex')
+    counter = Counter.new(32, prefix = ct[:12], initial_value = 0)
+    cipher = AES.new(key_.encode(), AES.MODE_CTR, counter = counter)
+    return cipher.decrypt(ct[16:]).decode()
+
+
+

示例代码

+

前端

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
let crypto = require("crypto")
+
+export function aesEncrypted(key, text) {
+  let iv = Buffer.concat([ crypto.randomBytes(12), Buffer.alloc(4, 0) ])
+  let cipher = crypto.createCipheriv("aes-128-ctr", key, iv)
+  return iv.toString('hex') + cipher.update(text, 'utf8', 'hex') + cipher.final('hex')
+}
+
+
+

后端

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+
+
from base64 import b64encode
+from Crypto.Cipher import AES
+from Crypto.Util import Counter
+from random import randint
+import codecs
+import hashlib
+
+
+def convert_to_md5(info: str) -> str:
+    """
+    md5加密
+    :param info: 需要加密的内容
+    :return: md5加密密文
+    """
+    md5 = hashlib.md5()
+    md5.update(info.encode('utf-8'))
+    return md5.hexdigest()
+
+
+def aesCreateKey() -> str:
+    """
+    生成aes加密的key,key的长度必须16位
+    :return: 返回key的base64密文
+    """
+    en_key = convert_to_md5(str(randint(100000, 999999)))[8:-8]
+    return b64encode(en_key.encode()).decode()
+
+
+def aesDecryption(key_: str, de_text: str) -> str:
+    """
+    aes解密函数
+    :param key_: aes的key
+    :param de_text: aes加密的密文
+    :return: 解密的文本
+    """
+    ct = codecs.decode(de_text.encode(), 'hex')
+    counter = Counter.new(32, prefix = ct[:12], initial_value = 0)
+    cipher = AES.new(key_.encode(), AES.MODE_CTR, counter = counter)
+    return cipher.decrypt(ct[16:]).decode()
+
+
+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover.jpg" "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover.jpg" new file mode 100644 index 0000000..799e6b6 Binary files /dev/null and "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover.jpg" differ diff --git "a/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_120x120_fill_q75_box_smart1.jpg" "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_120x120_fill_q75_box_smart1.jpg" new file mode 100644 index 0000000..b383811 Binary files /dev/null and "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_120x120_fill_q75_box_smart1.jpg" differ diff --git "a/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_1600x0_resize_q75_box.jpg" "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_1600x0_resize_q75_box.jpg" new file mode 100644 index 0000000..cb126a5 Binary files /dev/null and "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_1600x0_resize_q75_box.jpg" differ diff --git "a/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_800x0_resize_q75_box.jpg" "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_800x0_resize_q75_box.jpg" new file mode 100644 index 0000000..28bd82a Binary files /dev/null and "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/cover_hu289f93b8ec41ea81055c11038b70f240_213994_800x0_resize_q75_box.jpg" differ diff --git "a/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/index.html" "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/index.html" new file mode 100644 index 0000000..19db554 --- /dev/null +++ "b/p/\345\246\202\344\275\225\345\277\253\351\200\237\345\205\245\351\227\250\346\226\260\347\232\204\347\274\226\347\250\213\350\257\255\350\250\200\345\222\214\346\241\206\346\236\266/index.html" @@ -0,0 +1,710 @@ + + + + +如何快速入门新的编程语言和框架? + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 如何快速入门新的编程语言和框架? + + +
+ + +
+ + +
+

+ 如何快速入门新的编程语言和框架? +

+ + +

+ 最近很多小伙伴儿通过各种途径咨询我关于学习技术方面的问题,我都一一回复了。这里也专门出一个总结,关于我个人这些年入门新的编程语言和框架的经验和技巧... +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

一. 前言

+

最近很多小伙伴儿咨询我关于学习技术方面的问题,我都一一回复了。这里也专门出一个总结,关于我个人这些年入门新的编程语言和框架的经验和技巧,来跟大家分享一下,希望能够所有帮助。

+

本次分享分为两个时期:

+
    +
  • 有编程经验时期
  • +
  • 新手时期
  • +
+

二. 有编程经验时期

+

当我有了一定开发经验后,去学习一个新的编程语言或者框架,我会注意如下两点:

+

1. 寻找共性

+

编程语言

+

所有的编程语言或者框架,都不是凭空产生的。当前比较流行的语言,大都是类C语言,比如我们常见的Java、Python、JavaScript、Golang等等。

+

所以,我们学习的编程语言,除了特有的特性,大都是相似的,也就是它们的共性。我们只要理解和掌握这些类C语言的共性,就可以很快地掌握一门新的编程语言。

+

那么,哪些是共性呢?比如说变量、常量、运算符、判断、循环、函数、面向对象等等。

+

我们在学习一门新的编程语言的时候,是不是必然要学习如上这些共性的知识点?

+

那么,当你已经有一个编程语言的基础后,当你在学习和写代码的时候,这些共性可以直接使用你已学会的编程语言的知识去代入。

+

举个例子,比如我已经会Go了(不过,当你先学Go的话,可能不太知道面向对象是什么/狗头),要去学一下Python。

+

我需要做的就是代入:

+
    +
  • +

    Go语言声明变量是a := 1,考虑到Python没有强类型,那么就直接a = 1就完事了。

    +
  • +
  • +

    Go语言声明函数是func aFunction(username String) {},那么Python不过是换了个关键词,去掉了强类型,变成def aFunctino(username):,当然Python如果要申明类型也是可以的,写成def login(username: str):

    +
  • +
  • +

    其中,比如运算符大都是一样的,判断也是类似。

    +
  • +
  • +

    但是突然你发现,怎么Python的循环不一样,然后发现Python的循环好方便,但是其实原理还是和Go一样的。甚至,当你遇到不知道怎么处理的时候,直接来一波for (i := 0; i < count; i++)。当然,Python没法这么写。

    +
  • +
+

除了我举的这些例子,还有比如正则、数据类型、数据结构等等,都可以作为共性来代入。

+

框架

+

其实,框架也是如此。此处以web框架为例,比如Python的Flask、Django、FastApi,Go的Gin,Node的express,Java的SpringBoot等,都是比较熟知的框架。

+

这些Web框架,都是依托于网络的,大都是http(s)请求,甚至其他TCP请求。我们可以只在网络层面来看,它们共性就是request和response。所以在网络层面就可以理解为,后端web框架就是一个接收request和response的东西。

+

那么,看具体框架的功能上,可以抽象出路由、中间件。

+

所以,对于一个web框架,其共性为request、response、路由、中间件。

+

因此,我们不管是使用Flask、Gin还是SpringBootd等,可以使用我们已有的框架知识去代入:

+
    +
  • 如何接收request,比如获取request body、headers等
  • +
  • 如何使用中间件处理,比如使用中间件鉴权、传递上下文等
  • +
  • 如何配置路由,让接口对外暴露
  • +
  • 如何返回response,比如返回http status code、json等
  • +
+

那么,就可以完成一个新的web框架的入门了。

+

其它

+

除了编程语言和框架,其实还有很多别的都是拥有共性,可以快速入门的,比如Nginx和Apache,比如Linux发行版本,比如Kong和ApiSix,比如不同的数据库等等。甚至最近我发现,wasm并不是前端特有的,EBPF也在用wasm。

+

2. 看官方文档

+

编程语言的情况还比较少,更多是学习框架时,一定要看官方文档。

+

网上虽然有很多教程,但是毕竟是二次加工的,可能存在信息遗漏、版本更新等等问题。

+

官方文档可能比较生涩,但是它是最全最新的一手文档,会非常有助于少走弯路。

+

再次强调,一定要看官方文档!

+

二. 新手时期

+

当我还是新手的时候,我其实也常常苦恼于入门——教程看了好多遍,跟着教程敲了一遍又一遍,感觉还是只会基础,自己想写点什么却不会。

+

后来,当我入门一段时间后,我返回去想一想,才想明白一些技巧。

+

所以,以我的经历,总结如下步骤:

+

1. 仅学基础

+

一开始的时候,我也是不停地啃教程,不管是文档还是视频,但是发现我还是只会基础理论知识,那些高级特性案例都敲了,却并不明白什么意思。

+

后来,我就发现,对于我而言,并不需要学太多,只要学会基础即可,甚至基础也不用太深入。

+

2. 自己构思写一个项目

+

跟着教程敲代码,效果甚微。

+

要自己构思一个项目,比如写一个TODO,写一个命令行聊天工具,写一个博客等等简单的程序。最好是自己感兴趣的领域。

+

但是,需要注意的是,一定要自己去构思如何写:

+
    +
  • 你准备写一个什么程序?要达到什么效果?
  • +
  • 写这个程序有哪些功能?
  • +
  • 这些功能都应该怎么实现?
  • +
+

然后,直接开始写代码!哪怕只是创建了几个文件。

+

只有自己思考的,才能深刻理解和记忆。

+

3. 不会就去学,不懂就复制过来用

+

当然,因为仅仅学了基础,你会发现很多功能并不会,甚至比如如何读取一个文件都不会。

+

如果这时候,遇到不会的,就当场去学,哪怕是高级特性,学到把不会的这个点能做出来为止。

+

如果一个功能不懂什么意思,搞不明白,那么直接复制过来,调试到能满足功能为止,然后将这段代码和这个场景记住。

+

4. 多写代码,迈过门槛

+

我一直觉得写代码有一个门槛,在迈过门槛前,学习会非常困难,但是,一旦迈过这个门槛,你就会发现,学起来异常轻松,你所学的技术可以(相对)融会贯通。

+

那么迈过这个门槛的方法就是:重复2和3两个步骤,多写代码,多思考。

+

是的,不断重复,然后可能某一天,当你对你学的技术恍然大悟的时候,那么其实就是迈过了那个门槛。

+

然后,之后便是更深入地学习了。

+

三. 总结

+

一路走来,我觉得最重要的是“多写代码,多思考”。不少代码写到最后甚至都是肌肉记忆了,但是代码还是会变的,自己思考的方式和过程,自己沉淀的知识才是最宝贵的,足以支撑我能够不断学习新的技术,适应新的模式。

+ +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\346\265\217\350\247\210\345\231\250\344\270\212ts\345\256\236\347\216\260\345\211\215\347\253\257\347\233\264\344\274\240minio/index.html" "b/p/\346\265\217\350\247\210\345\231\250\344\270\212ts\345\256\236\347\216\260\345\211\215\347\253\257\347\233\264\344\274\240minio/index.html" new file mode 100644 index 0000000..81cb7bd --- /dev/null +++ "b/p/\346\265\217\350\247\210\345\231\250\344\270\212ts\345\256\236\347\216\260\345\211\215\347\253\257\347\233\264\344\274\240minio/index.html" @@ -0,0 +1,935 @@ + + + + +浏览器上ts实现前端直传minio + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ 浏览器上ts实现前端直传minio +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

前端从后端获取到sts,然后直接minio,极大减少服务端的压力。

+

当然,肯定会这种疑问,为什么不在后端生成临时签名url,给前端上传/下载呢?问就是业务需要 /狗头……

+

环境

+
    +
  • minio: ^7.0.32
  • +
  • typescript: 4.9.5
  • +
+

浏览器上的坑

+

为什么标题上要强调“浏览器”呢?

+

这是因为官方的minio.js,感觉当前版本并没有考虑浏览器的使用场景,无法直接在浏览器上直传。

+

所以这里就是推荐一个折中的方案,可以在前端直传。

+

当然了,如果业务允许,当前版本请直接在后端生成临时签名url吧!

+

minio直传代码实现

+

安装minio.js

+
+ +
+
1
+
+
pnpm add -D minio
+
+
+

生成client

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
import * as Minio from 'minio'
+
+const minioClient = new Minio.Client({
+  region: 'cn-north-1', // region字段,极其有必要加上!我在sts场景,不加region字段就报错权限不够/心累...
+  endPoint: '192.168.1.1', // minio的地址
+  port: 9000, // minio端口
+  useSSL: true, // 是否使用ssl
+  accessKey,
+  secretKey,
+  sessionToken, // 可选字段,当为sts时,加入此字段
+})
+
+
+

putObject直传方案

+
+ +
+
1
+
+
putObject(bucketName, objectName, stream)
+
+
+

官方提供的putObject方法,必填这三个字段,前两个很好理解,主要是第三个stream,需要详细看一下。

+

stream的类型是:string | internal.Readable | Buffer

+

string很好搞定,直接putObject(bucketName, 'hello.txt', 'hello,world!')这样子上传文本文件。

+

但是另外两个类型都是nodejs的啊…啊这…(也许是我错了,有大佬能够解决的请务必直接告诉我…)

+

折中方案

+

那么折中的方案就是由前端生成临时签名url,再由前端进行上传 /哭。

+
+ +
+
1
+
+
const url = await minioClient.presignedPutObject(bucketName, filename)
+
+
+

然后利用http请求url进行上传即可。虽然比较曲折,但是目前相对比较好的前端解决方案。

+

http请求url上传

+

此处介绍下http请求上传的相关操作。

+

请求上传

+

请求上传可以有三种方案。

+

XMLHttpRequest

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
const xhrUploadFile = (file: File, url: string) => {
+  const xhr = new XMLHttpRequest();
+  xhr.open('PUT', url, true);
+  xhr.send(file);
+  xhr.onload = () => {
+    if (xhr.status === 200) {
+      console.log(`${file.name} 上传成功`);
+    } else {
+      console.error(`${file.name} 上传失败`);
+    }
+  };
+}
+
+
+

Fetch

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
const fetchUploadFile = (file: File, url: string) => {
+  fetch(url, {
+    method: 'PUT',
+    body: file,
+  })
+    .then((response) => {
+      console.log(`${file.name} 上传成功`, response);
+    })
+    .catch((error) => {
+      console.error(`${file.name} 上传失败`, error);
+    });
+}
+
+
+

Axios

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+
+
const axiosUploadFile = (file: File, url: string) => {
+  const instance = axios.create();
+  instance
+    .put(url, file, {
+      headers: {
+        'Content-Type': file.type,
+      },
+    })
+    .then(function (response) {
+      console.log(`${file.name} 上传成功`, response);
+    })
+    .catch(function (error) {
+      console.error(`${file.name} 上传失败`, error);
+    });
+}
+
+
+

Promise

+

此处可以封装一下请求,叠加promisebuff,此处以XMLHttpRequest为例:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
const uploadRequest = (file: File, url: string) => {
+  return new Promise((resolve, reject) => {
+    const xhr = new XMLHttpRequest()
+    xhr.open('PUT', url, true)
+    xhr.send(file)
+    xhr.onload = () => {
+      if (xhr.status === 200)
+        resolve(xhr.response)
+      else
+        reject(xhr.status)
+    }
+  })
+}
+
+
+

事件响应

+

要完成上传,怎么能没有响应事件呢。

+

此处的事件包括:

+
    +
  • 上传进度
  • +
  • 上传完成
  • +
  • 上传失败
  • +
+

请求代码如下:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
+
const uploadRequest = async (file: File, url: string) => {
+  const xhr = new XMLHttpRequest()
+
+  xhr.upload.addEventListener('progress', (e) => {
+    onUploadProgress(`${((e.loaded / e.total) * 100).toFixed(0)}`) // 更新进度,此处不保留小数点
+  })
+
+  xhr.onload = () => {
+    if (xhr.status === 200) {
+      try {
+        onUploaded() // 响应上传完成事件
+      }
+      catch (error) {
+        onUploadErr((error as Error).message) // 响应上传错误,此处ts处理error
+      }
+    }
+    else { onUploadErr(`http code is ${xhr.status}`) } // 响应上传错误
+  }
+
+  xhr.open('PUT', url, true)
+  xhr.send(file)
+}
+
+
+

取消上传

+
+ +
+
1
+2
+3
+
+
const xhr = new XMLHttpRequest()
+// ...
+xhr.abort() // 取消上传
+
+
+

性能优化

+
    +
  • 对于大文件,或者说需要后台运行的上传任务,可以使用web worker来跑
  • +
  • 对于大文件,可以使用切片的方式提高并发,切片的方式也可以实现断点续传,只是需要注意文件切片的顺序和唯一id
  • +
+

参考文档

+ + +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover.png" "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover.png" new file mode 100644 index 0000000..4958680 Binary files /dev/null and "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover.png" differ diff --git "a/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_120x120_fill_box_smart1_3.png" "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_120x120_fill_box_smart1_3.png" new file mode 100644 index 0000000..1268e48 Binary files /dev/null and "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_120x120_fill_box_smart1_3.png" differ diff --git "a/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_1600x0_resize_box_3.png" "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_1600x0_resize_box_3.png" new file mode 100644 index 0000000..916d334 Binary files /dev/null and "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_1600x0_resize_box_3.png" differ diff --git "a/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_800x0_resize_box_3.png" "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_800x0_resize_box_3.png" new file mode 100644 index 0000000..7f3669a Binary files /dev/null and "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_800x0_resize_box_3.png" differ diff --git "a/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/index.html" "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/index.html" new file mode 100644 index 0000000..22ee117 --- /dev/null +++ "b/p/\347\250\213\345\272\217\345\221\230\345\246\202\344\275\225\345\277\253\351\200\237\351\252\214\350\257\201\344\270\232\345\212\241\351\234\200\346\261\202/index.html" @@ -0,0 +1,671 @@ + + + + +程序员如何快速验证业务需求? + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 程序员如何快速验证业务需求? + + +
+ + +
+ + +
+

+ 程序员如何快速验证业务需求? +

+ + +

+ 作为一个程序员,听着攒劲的小曲,写着优雅的代码,加班加点终于写完了迭代中分配的模块需求。然后你跟别的同事一联调,却发现各种问题... +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

设想一个场景:

+

你作为一个程序员,听着攒劲的小曲,写着优雅的代码,加班加点终于写完了迭代中分配的模块需求。然后你跟别的同事一联调,却发现各种问题。

+

你跟同事A说:“你得在这种情况下给我数据A。“

+

同事A一脸懵逼:“这种情况需求也没写啊,在这种情况下我也没有数据A啊!“

+

然后你和同事A一起顺着业务流程,找到了负责上层模块的同事B。

+

同事B一脸懵逼:“这个迭代中我没有这个模块的开发需求啊,这种情况下我也没有数据A啊!”

+

然后你和同事A、同事B一起顺着业务流程,找到了负责上层模块的……

+

最后,你和同事ABC…一起找到了产品,产品一脸懵逼:“那没办法,就改需求吧……”

+

你眼看着所剩无几的迭代时间,生无可恋地掏出手机,发个微信取消了这周末的相亲…

+

不知道程序员xdm有没有经历过这样的场景,如果各位程序员xd没有经历过这样的场景,那真的是恭喜你了,且行且珍惜!从毕业后参加工作开始,我是真的经历太多了,太痛了!

+

我之后不断学习各种方法,尝试去破解这样的困局。虽然说,工作中的问题很多,但是能解决一个是一个,后面再慢慢跟各位分享其他问题中我的解决方案。

+

所以,本篇文章将分享一波关于这类问题我的解决方案。谨代表个人主观观点。

+

痛点

+

需求是用户的痛点,但是在这个场景下,是程序员的痛点…

+

那么,仔细分析一下,这个场景下的痛点,到底是什么呢?我认为是如下两点:

+
    +
  • 产品给到的需求无法自洽,且场景覆盖不够
  • +
  • 开发团队的技术评审和技术文档不到位
  • +
  • 开发各自按照模块开发,忽略外部环境,仅仅当各自开发完成后联调时候才会组装应用,发现问题
  • +
+

当然,可能有程序员xd会疑问:这种问题不是靠严谨的开发流程就可以避免吗?

+

理论上来说是这样的,但是以我个人经历而言,不管是大团队还是小团队,总有很多不稳定的因素,比如说迭代周期、协作流程的规范性等等。

+

特别是在创业团队,或者有些项目特别着急的时候,经常是没有足够的时间给到团队的各个角色去充分准备的,甚至为了赶时间,会精简一些环节甚至直接去掉。这种时候出现这种问题导致改需求,往往是非常可怕的,后果也往往是开发团队加班。

+

解决方案

+

我目前发现且实践后效果较好的方案,就是“曳光弹式开发”。

+

什么是曳光弹

+

经常关注军事的小伙伴儿,应该了解,曳光弹是一种运用于军事场景的特殊子弹。

+

我们都知道武器发射时需要瞄准,比如枪就通过照门和准星来瞄准,而坦克和装甲车以及飞机都有专用的瞄准具,非常复杂。对于轻武器来说,一般攻击距离都比较近,使用自带的机械瞄准具或者外加的光学瞄准镜都足够,但是在距离稍远的时候瞄准镜显然不够。而对于飞机和战斗机来讲,在空中飞行时飞行姿态变化多样,有时候进攻的时间很短,需要快速射击并且调整弹道,这时候就需要可以发光的曳光弹。

+

曳光弹正如其名,可以发光而且指示弹道。曳光弹的结构比一般子弹更加复杂。子弹的弹壳部分和一般子弹一样,前半部分是钢心或者铅心的弹头,但是在后部有一个空腔,一般称作曳光管,里面填充着曳光剂。曳光剂的成分还比较复杂,一般来说主要成分是镁粉和铝镁合金粉,用来燃烧,除此之外还有硝酸锶。这样一来,燃烧的时候硝酸锶就会发出红光。大家平时看到的很多曳光弹还有黄光和绿光,加入钠盐就会发出黄光,而加入铜盐就会发出绿光。除此之外还在表面加入一层过氧化钡,以保证曳光剂被点燃。

+

对于机枪手来说,如果射击距离较远,自然不能选择过于精确的设计方式,也就是说不能靠瞄准具来射击。而一般的机枪主要起压制和面杀伤作用,加入曳光弹就是机枪手更加快速方便的控制弹道,随时变化攻击的方向。如果没有曳光弹的话,射手根本无法发现自己的子弹弹道,也就很难去调整弹道。毕竟枪械射击温度升高之后,弹道会有所变化,不同的弹药不同的枪管也都会改变子弹的弹道,如果仅仅依靠枪械自身的瞄准具去调整反而会适得其反,而曳光弹就很好的解决了这个问题。

+

在开发中的作用

+

理解曳光弹本身在军事中的作用后,我们再来看曳光弹式开发如何在开发中起作用。

+

开发团队在接手到产品给到的业务需求后,特别是构建一些以前从未做过的东西时,对这个产品/功能的最终成效是模糊的。

+

程序员就像坦克上的机枪手一样,在尝试在黑暗中击中目标。但是如果像文章一开始的时候,大家先埋头写自己的模块,然后再联调,最终发现问题,感觉就像一上战场,大家都朝着各自理解的方向拼命清空弹夹,击中目标的寥寥无几,最后受伤的还是自己。

+

所以,此时,就需要先使用几颗曳光弹,去击中目标,在黑暗中划出轨迹,开发团队再调整方向,对着目标集中火力。

+

如何应用

+

非常简单!步骤如下:

+
    +
  1. 找出级别最高的需求
  2. +
  3. 以完成最基础的完整功能为目标,从前端到部署,开发一个可以运行的骨架
  4. +
  5. 最后上相关需求的测试案例
  6. +
+

这样,射出一颗曳光弹,穿透客户端、后端、数据库、运维、测试等不同层面。一旦击中目标——即符合用户需求,后续的任务便大都是搬砖的活儿,去丰富这个骨架。

+

曳光弹并不是总能击中目标的,中途若是发现未能击中目标,便可在前期以极小成本去调整曳光弹的方向,继续发射曳光弹。

+

如果你要问,什么是“最基础的完整功能”,那么可以这么说,那么举个例子:前端不要任何样式,直接能够满足比如表单功能即可,后端不用任何校验,能够处理和传递数据即可。

+

总结

+

为了解决文章开头的问题,需要使用曳光弹式开发,先开发一个骨架,确认满足需求后,即可继续丰富骨架,完成产品。

+

作为团队的一员,团队的每一个角色都很重要,程序员跟产品也是需要紧密协作,互帮互助,才能使得产品更好,也使得业绩更好。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover.png" "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover.png" new file mode 100644 index 0000000..3ab679a Binary files /dev/null and "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover.png" differ diff --git "a/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_120x120_fill_box_smart1_3.png" "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_120x120_fill_box_smart1_3.png" new file mode 100644 index 0000000..e0b586e Binary files /dev/null and "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_120x120_fill_box_smart1_3.png" differ diff --git "a/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_1600x0_resize_box_3.png" "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_1600x0_resize_box_3.png" new file mode 100644 index 0000000..e800724 Binary files /dev/null and "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_1600x0_resize_box_3.png" differ diff --git "a/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_800x0_resize_box_3.png" "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_800x0_resize_box_3.png" new file mode 100644 index 0000000..95f98a1 Binary files /dev/null and "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_800x0_resize_box_3.png" differ diff --git "a/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/index.html" "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/index.html" new file mode 100644 index 0000000..419f546 --- /dev/null +++ "b/p/\350\243\270\350\276\236\345\220\216\347\213\254\347\253\213\345\274\200\345\217\221\344\272\247\345\223\201\344\270\212\347\272\277\344\272\224\345\244\251\345\274\200\345\247\213\347\233\210\345\210\251\346\210\221\346\230\257\346\200\216\344\271\210\345\201\232\347\232\204\345\210\206\344\272\253\346\210\221\347\232\204\350\277\207\347\250\213\344\270\212/index.html" @@ -0,0 +1,778 @@ + + + + +裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上) + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上) + + +
+ + +
+ + +
+

+ 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上) +

+ + +

+ 分享一个普通程序员裸辞后第一次尝试独立开发的过程... +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

前言

+

裸辞后,我花了一个多月开发我的第一个独立开发产品RedisMate。其MVP版本上线五天,产品从没有排名,到冲到了国区mac app store开发工具类排名榜的前十,并持续有了几笔收入。

+

那么,我是怎么做的呢?

+

我是一个正在尝试独立开发的普通程序员,在此跟各位分享我的过程,不仅是我对自己的复盘,还希望能够给一些正在或准备尝试独立开发的兄弟一点参考。所有的过程都是按照我主观的想法去推进执行的,如果有更好的建议,欢迎交流!

+

本次分享分为上下两篇文章,分别为产品设计和产品推广两个主题。

+

起源

+

首先,我并不是因为我有一个很棒的点子或产品,而选择去裸辞,去独立开发。

+

而是我因为别的原因选择了裸辞,然后开始把独立开发作为未来的一条路,并尝试去走。

+

所以,也可以说,一开始我是为了独立开发而去独立开发。

+

产品选择

+

既然我没有一个很棒的点子去完成一个产品,那么我就开始去找。

+

自身条件

+

首先,为了能够独立开发,我要审视自身,我会什么?

+

罗列下我的开发技能:

+
    +
  • 前端:Vue、TS、…
  • +
  • 后端:Go、Python、Rust
  • +
  • 数据库:MySQL、Redis、PostgreSQL、ClickHouse、…
  • +
  • 运维:Docker、MQ、Nginx、…
  • +
  • 客户端/原生:Electron、Flutter、SwiftUI
  • +
  • 硬件:esp32
  • +
+

检查下其它独立开发必需技能:

+
    +
  • 产品:自嗨水平
  • +
  • 设计:自嗨水平
  • +
  • 运营:几乎没有
  • +
+

罗列下我熟悉的可以寻找需求的领域:

+
    +
  • 开发
  • +
  • 玩游戏
  • +
  • Linux日常办公
  • +
  • Mac日常办公
  • +
+

我能在独立开发过程中投入多少资源:

+
    +
  • 时间:全职
  • +
  • 资金:起初的时候几乎完全不想投入一块钱,但起码要支付苹果个人开发者年费+服务器年费+域名年费
  • +
+

产品形态

+

那么,基于开发技能,得出我可以做的产品的形态:

+
    +
  • 安卓app
  • +
  • ios app
  • +
  • Windows/Linux/macOS应用程序
  • +
  • SaaS
  • +
  • 硬件
  • +
  • 浏览器插件
  • +
  • 编辑器插件
  • +
  • 微信小程序
  • +
+

但是考虑到希望以较小的成本来尝试独立开发,和希望能够短期得到收入来验证产品,那么就剩下了

+
    +
  • 安卓app
  • +
  • ios app
  • +
  • Windows/Linux/macOS应用程序
  • +
+

并且还都是离线应用,不需要服务器的支撑,没有太多服务器的额外成本,服务器仅仅需要最小配置用来备案即可。

+

考虑到付费方式的成本、上架的成本,再排除掉安卓app、Windows/Linux应用程序。

+

那么就只剩下

+
    +
  • ios app
  • +
  • macOS应用程序
  • +
+

产品需求

+

确定了产品形态,我就要考虑,到底要做什么产品呢?产品需求是什么?

+

如果我从当下很火但我完全不熟悉的领域去找需求,那我肯定没法挖掘真正的需求。

+

如果我找一个熟悉某个领域的人咨询,他并没有挖掘需求的能力,无法识别出真正的需求,这一点在我多年的工作中已经见识过太多太多次了,最后完完全全就是一个定制化项目,在市场上根本没有竞争力。

+

这就要从上述罗列的,我熟悉的领域去找。

+

所以我把目标放在了“开发”上。“开发”是我最熟悉的领域,在其中这么多年了,需求来源于我自己,我就是用户,我就可以直接验证这个需求是不是伪需求,这个功能是不是好功能。

+

并且,鉴于我三年前开发过一款开源的Redis GUI Client——RedisFish,积累了一些经验,并且后来因为工作搁置了,导致一直没有完成也心有遗憾。

+

所以最终决定开发一款Redis GUI for Mac。

+

市场调研

+

在决定产品之后,我先去问了一圈我的朋友和前同事,他们正在用的Redis GUI是什么。

+

得到的回复是如下产品:

+
    +
  • RedisDesktopManager
  • +
  • AnotherRedisDesktopManager
  • +
  • Medis2
  • +
  • RedisFish(被我安利用我自己的开源产品,哈哈…)
  • +
+

其中RedisDesktopManager和AnotherRedisDesktopManager是最多的,毕竟是老牌的强劲产品!

+

然后,我又去翻了些论坛和帖子,发现有些人在发帖问mac上有哪些推荐的Redis GUI,但是哪怕是老外,也是如下选项居多:

+
    +
  • RedisDesktopManager
  • +
  • AnotherRedisDesktopManager
  • +
  • Medis2
  • +
  • RedisInsight
  • +
+

接着,我也去了解了下近期新出的,比如Tiny RDM,听劝作品,真的很棒的,哈哈。

+

然后还有几个出现在比如gitee等平台的。

+

所以目前mac原生的也只有Medis2。

+

最后,我就要说那个经典台词了:没有能够满足我需求的!(不是…

+

重新构思MVP版本

+

到这个事件前,我已经开发完了既定的MVP版本,为v 1.1,此时的RedisMate是一个完成了基础Redis功能的App。

+

而我也已经怀着激动而又紧张的心情,在想如何推广MVP版本了。

+

但是,在我准备尝试推广的那个早上,在上厕所的时候,当时正在看《人性的弱点》。刚好看到书上讲了一个推销员的案例,推销员疯狂地讲述自己的产品怎么怎么好,让作者要赶紧买。

+

因此作者就在书上写道:

+
+

人们其实什么东西都不需要。如果我们想买点什么,早就出门去买回来了。人们真正需要的,是解决问题的方式。人类永远都面临着种种问题,永远都需要这些问题的解决方案——如果销售人员能够证明其服务或产品可以帮助人们解决问题,不用推销,我们就会主动掏钱。对消费者而言,“主动买”比“被推销”的感觉好得多。

+
+

所以,我就在想,我现在做的这是什么啊?!我能给其他人解决什么问题呢?凭什么要选择我的,而不是别的成熟产品?

+

我就开始重新构思MVP版本,希望能够加上独特的、创新的功能,能够真实解决用户问题的功能。

+

创新

+

寻找创新

+

如何发现问题并解决问题?可以从经济学的四种不同类型的效用来入手:

+
    +
  • 场所效益:使得原本无法接触的事物变得可接触
  • +
  • 形式效用:通过重新排列现有部件使某物更有价值
  • +
  • 时间工具:让事情变快的工具
  • +
  • 拥有权利:去除中间人
  • +
+

在此处,我的产品RedisMate上,最简单也是最快的的创新方式就是“让事情变得更快”。

+

我先明确Redis GUI使用频率最高的功能,然后从那个功能入手,让用户能够立竿见影感受到RedisMate的便捷。

+

那么,经过自己的总结,和跟朋友的沟通,我总结出,Redis GUI工具,使用频率从高到低的功能分别是:查(甚至是搜索)、删、改、增。

+

因此,我就从搜索功能入手。

+

我把RedisMate搜索功能的所有步骤罗列出来,然后思考,有哪些步骤可以减去?如何减少步骤?

+

最终就得到了现在“快速搜索”功能!

+

当Redis GUI窗口常驻时,一般最少需要约7步操作,那么在RedisMate上最少只要3步。

+

当Redis GUI仅打开没有连接任何服务器时,一般最少需要约9步操作,但是在RedisMate上最少只要3步。

+

当Redis GUI反复搜索固定key时,一般最少需要约4步,但是在RedisMate最少仅仅需要1步。

+

所以,我在RedisMate上针对搜索场景,进行了优化,并确实在推广时,得到了不少积极反馈。

+

并且,基于上述过程,优化了RedisMate的布局和一些细微的功能,让用户能够更直观更快去操作。

+

挖掘需求

+

等我后面回过神的时候,才发现我一直有这个需求。

+

经常性,我要前后端一起开发,由于是微服务,多个节点并发开发和调试,每次都要开超多窗口。

+

我经常要切换窗口时,要愣一会儿思考一下,我要切什么窗口来着…

+

所以我就一直在想,能不能省略掉这些窗口,窗口自己呼出然后切回去!

+

现在RedisMate就可以了,这就解决了我曾经的问题。

+

然后,我就又想到曾经的一个抱怨,有时候在调试时,代码有点问题,或者业务链路较长,需要联调时间有点长,或者MQ驱动的业务节点产生key有点慢等等,key在我查的时候没有出现。我就要反复调出Redis GUI然后点点点后搜索再点点点,发现不对!调一下代码,再来一遍!

+

我当时就在想,要是GUI能帮我盯着就好了。

+

所以,我设计了"Search Until Found"功能,在得到朋友的肯定后,将其加入到了MVP版本中 。

+

总结

+

首先,我很庆幸自己这些年学习和玩了不少技术,让我能够在想去独立开发的时候,不仅能够独立完成开发,还能有多种产品形态可以选择。并且我也有意识地去读技术之外的书,让我在其它领域有那么点点基础,只可惜没有学得更多。

+

其次,我觉得对我而言,苹果生态开发是当前成本最小的方式。

+

然后,我从我熟悉的领域入手,寻找自己的痛点,验证自己的需求,得到解决方案后,向身边的目标用户寻求验证。

+

最后,花一个多月开发完MVP版本,推出来验证自己的产品。

+

最后

+

感谢你看完这篇文章,希望我的经过能够给你一些参考,帮助到你。

+

本次分享的下一篇文章——产品推广方面,我正在努力写,敬请期待并关注我以获取更新。

+

如果你对我的产品RedisMate感兴趣,非常欢迎点击此处了解和下载,或者在mac app store中搜索“RedisMate“。

+

最后,要感谢各位道上兄弟的关照,给了很多支持和反馈。

+

还要特别感谢那些购买高级版的兄弟,非常感谢你们的支持!

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/page/1/index.html b/page/1/index.html new file mode 100644 index 0000000..84756b7 --- /dev/null +++ b/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/ + + + + + + diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 0000000..41f9091 --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,1261 @@ + + + + + +开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+
+ +
+ + +
+

+ python3 socket udp example +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ VUE3+TS+微前端实践 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ VUE项目国际化 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + + + + + + + +
+
+ +
+ + +
+

+ vue3 script setup响应式初体验 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + + + + + + +
+ + + + +
+
+ + + + + diff --git a/page/3/index.html b/page/3/index.html new file mode 100644 index 0000000..12b36e2 --- /dev/null +++ b/page/3/index.html @@ -0,0 +1,1261 @@ + + + + + +开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + + + + + + + +
+
+ +
+ + +
+

+ Golang实现农历转换阳历 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ IOS监听上下左右滑动手势 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + + + + +
+
+ +
+ + +
+

+ Golang AES-256-CBC加密和解密 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + + + +
+ + + + +
+
+ + + + + diff --git a/page/4/index.html b/page/4/index.html new file mode 100644 index 0000000..77ea689 --- /dev/null +++ b/page/4/index.html @@ -0,0 +1,1264 @@ + + + + + +开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + + + + +
+
+ +
+ + +
+

+ SwiftUI项目实现搜索功能 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ SwiftUI项目Image点击事件 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+
+ +
+ + +
+

+ Swift计算两个日期的天数差 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ Swift UI项目调用core data +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ H5检测手机摇一摇 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + +
+
+ +
+ + +
+

+ Promise inside request interceptor +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ tailwindcss基础 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+ + + + +
+
+ + + + + diff --git a/page/5/index.html b/page/5/index.html new file mode 100644 index 0000000..0150f39 --- /dev/null +++ b/page/5/index.html @@ -0,0 +1,1261 @@ + + + + + +开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+
+ +
+ + +
+

+ sqlx建模、连接与使用 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ gin中间件和鉴权 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ go-Redis的发布与订阅 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ go单元测试 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ gin的http单元测试 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ Flask设置全局错误捕获 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+
+ +
+ + +
+

+ Docker修改时区 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + +
+
+ +
+ + +
+

+ Golang使用JSON格式存取Redis +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ Go生成6位随机数 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+ + + + +
+
+ + + + + diff --git a/page/6/index.html b/page/6/index.html new file mode 100644 index 0000000..dca20b1 --- /dev/null +++ b/page/6/index.html @@ -0,0 +1,1261 @@ + + + + + +开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + + + + + + + +
+
+ +
+ + +
+

+ SFTP部署报错解决记录 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+
+ +
+ + +
+

+ docker中php上传大小限制 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + + + + +
+
+ +
+ + +
+

+ v-charts添加图表标题 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ vue组件props双向绑定 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ Flask_sqlalchemy的增删改查 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + +
+ + + + +
+
+ + + + + diff --git a/page/7/index.html b/page/7/index.html new file mode 100644 index 0000000..33ff01a --- /dev/null +++ b/page/7/index.html @@ -0,0 +1,730 @@ + + + + + +开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+
+ +
+ + +
+

+ Flask_Restful视图函数模块化 +

+ + +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + + + +
+ + + + +
+
+ + + + + diff --git a/page/index.html b/page/index.html new file mode 100644 index 0000000..f018af9 --- /dev/null +++ b/page/index.html @@ -0,0 +1,559 @@ + + + + +Pages + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

3 pages

+

Pages

+ +
+
+
+ +
+ + + + + + + +
+ + + +
+
+ + + + + diff --git a/page/index.xml b/page/index.xml new file mode 100644 index 0000000..6444225 --- /dev/null +++ b/page/index.xml @@ -0,0 +1,50 @@ + + + + Pages on 开发者小橙 + https://blog.hunterji.com/page/ + Recent content in Pages on 开发者小橙 + Hugo -- gohugo.io + en-us + Sun, 06 Mar 2022 00:00:00 +0000 + Archives + https://blog.hunterji.com/archives/ + Sun, 06 Mar 2022 00:00:00 +0000 + + https://blog.hunterji.com/archives/ + + + + About + https://blog.hunterji.com/about/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://blog.hunterji.com/about/ + <p>Hello, 我是开发者小橙。</p> +<p><img src="https://blog.hunterji.com/about/weixin_qr.jpg" + width="258" + height="258" + srcset="https://blog.hunterji.com/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_480x0_resize_q75_box.jpg 480w, https://blog.hunterji.com/about/weixin_qr_huf655d6c2f3fb884bc5e38550f20b90e7_27494_1024x0_resize_q75_box.jpg 1024w" + loading="lazy" + + alt="公众号 - 开发者小橙" + + + class="gallery-image" + data-flex-grow="100" + data-flex-basis="240px" + +></p> + + + + Search + https://blog.hunterji.com/search/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://blog.hunterji.com/search/ + + + + + diff --git a/page/page/1/index.html b/page/page/1/index.html new file mode 100644 index 0000000..378be16 --- /dev/null +++ b/page/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/page/ + + + + + + diff --git a/post/index.html b/post/index.html new file mode 100644 index 0000000..c463921 --- /dev/null +++ b/post/index.html @@ -0,0 +1,768 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

75 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/post/index.xml b/post/index.xml new file mode 100644 index 0000000..c2ef898 --- /dev/null +++ b/post/index.xml @@ -0,0 +1,15150 @@ + + + + Posts on 开发者小橙 + https://blog.hunterji.com/post/ + Recent content in Posts on 开发者小橙 + Hugo -- gohugo.io + en-us + Wed, 24 Jan 2024 23:53:17 +0000 + 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上) + https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/ + Wed, 24 Jan 2024 23:53:17 +0000 + + https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/ + <img src="https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/cover.png" alt="Featured image of post 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上)" /><h2 id="前言">前言</h2> +<p>裸辞后,我花了一个多月开发我的第一个独立开发产品<a class="link" href="https://redismate.hunterji.com/" target="_blank" rel="noopener" + >RedisMate</a>。其MVP版本上线五天,产品从没有排名,到冲到了国区mac app store开发工具类排名榜的前十,并持续有了几笔收入。</p> +<p>那么,我是怎么做的呢?</p> +<p>我是一个正在尝试独立开发的普通程序员,在此跟各位分享我的过程,不仅是我对自己的复盘,还希望能够给一些正在或准备尝试独立开发的兄弟一点参考。所有的过程都是按照我主观的想法去推进执行的,如果有更好的建议,欢迎交流!</p> +<p>本次分享分为上下两篇文章,分别为产品设计和产品推广两个主题。</p> +<h2 id="起源">起源</h2> +<p>首先,我并不是因为我有一个很棒的点子或产品,而选择去裸辞,去独立开发。</p> +<p>而是我因为别的原因选择了裸辞,然后开始把独立开发作为未来的一条路,并尝试去走。</p> +<p>所以,也可以说,一开始我是为了独立开发而去独立开发。</p> +<h2 id="产品选择">产品选择</h2> +<p>既然我没有一个很棒的点子去完成一个产品,那么我就开始去找。</p> +<h3 id="自身条件">自身条件</h3> +<p>首先,为了能够独立开发,我要审视自身,我会什么?</p> +<p>罗列下我的开发技能:</p> +<ul> +<li>前端:Vue、TS、&hellip;</li> +<li>后端:Go、Python、Rust</li> +<li>数据库:MySQL、Redis、PostgreSQL、ClickHouse、&hellip;</li> +<li>运维:Docker、MQ、Nginx、&hellip;</li> +<li>客户端/原生:Electron、Flutter、SwiftUI</li> +<li>硬件:esp32</li> +</ul> +<p>检查下其它独立开发必需技能:</p> +<ul> +<li>产品:自嗨水平</li> +<li>设计:自嗨水平</li> +<li>运营:几乎没有</li> +</ul> +<p>罗列下我熟悉的可以寻找需求的领域:</p> +<ul> +<li>开发</li> +<li>玩游戏</li> +<li>Linux日常办公</li> +<li>Mac日常办公</li> +</ul> +<p>我能在独立开发过程中投入多少资源:</p> +<ul> +<li>时间:全职</li> +<li>资金:起初的时候几乎完全不想投入一块钱,但起码要支付苹果个人开发者年费+服务器年费+域名年费</li> +</ul> +<h3 id="产品形态">产品形态</h3> +<p>那么,基于开发技能,得出我可以做的产品的形态:</p> +<ul> +<li>安卓app</li> +<li>ios app</li> +<li>Windows/Linux/macOS应用程序</li> +<li>SaaS</li> +<li>硬件</li> +<li>浏览器插件</li> +<li>编辑器插件</li> +<li>微信小程序</li> +</ul> +<p>但是考虑到希望以较小的成本来尝试独立开发,和希望能够短期得到收入来验证产品,那么就剩下了</p> +<ul> +<li>安卓app</li> +<li>ios app</li> +<li>Windows/Linux/macOS应用程序</li> +</ul> +<p>并且还都是离线应用,不需要服务器的支撑,没有太多服务器的额外成本,服务器仅仅需要最小配置用来备案即可。</p> +<p>考虑到付费方式的成本、上架的成本,再排除掉安卓app、Windows/Linux应用程序。</p> +<p>那么就只剩下</p> +<ul> +<li>ios app</li> +<li>macOS应用程序</li> +</ul> +<h3 id="产品需求">产品需求</h3> +<p>确定了产品形态,我就要考虑,到底要做什么产品呢?产品需求是什么?</p> +<p>如果我从当下很火但我完全不熟悉的领域去找需求,那我肯定没法挖掘真正的需求。</p> +<p>如果我找一个熟悉某个领域的人咨询,他并没有挖掘需求的能力,无法识别出真正的需求,这一点在我多年的工作中已经见识过太多太多次了,最后完完全全就是一个定制化项目,在市场上根本没有竞争力。</p> +<p>这就要从上述罗列的,我熟悉的领域去找。</p> +<p>所以我把目标放在了“开发”上。“开发”是我最熟悉的领域,在其中这么多年了,需求来源于我自己,我就是用户,我就可以直接验证这个需求是不是伪需求,这个功能是不是好功能。</p> +<p>并且,鉴于我三年前开发过一款开源的Redis GUI Client——<a class="link" href="https://github.com/hunter-ji/RedisFish" target="_blank" rel="noopener" + >RedisFish</a>,积累了一些经验,并且后来因为工作搁置了,导致一直没有完成也心有遗憾。</p> +<p>所以最终决定开发一款Redis GUI for Mac。</p> +<h2 id="市场调研">市场调研</h2> +<p>在决定产品之后,我先去问了一圈我的朋友和前同事,他们正在用的Redis GUI是什么。</p> +<p>得到的回复是如下产品:</p> +<ul> +<li>RedisDesktopManager</li> +<li>AnotherRedisDesktopManager</li> +<li>Medis2</li> +<li>RedisFish(被我安利用我自己的开源产品,哈哈&hellip;)</li> +</ul> +<p>其中RedisDesktopManager和AnotherRedisDesktopManager是最多的,毕竟是老牌的强劲产品!</p> +<p>然后,我又去翻了些论坛和帖子,发现有些人在发帖问mac上有哪些推荐的Redis GUI,但是哪怕是老外,也是如下选项居多:</p> +<ul> +<li>RedisDesktopManager</li> +<li>AnotherRedisDesktopManager</li> +<li>Medis2</li> +<li>RedisInsight</li> +</ul> +<p>接着,我也去了解了下近期新出的,比如Tiny RDM,听劝作品,真的很棒的,哈哈。</p> +<p>然后还有几个出现在比如gitee等平台的。</p> +<p>所以目前mac原生的也只有Medis2。</p> +<p>最后,我就要说那个经典台词了:没有能够满足我需求的!(不是&hellip;</p> +<h2 id="重新构思mvp版本">重新构思MVP版本</h2> +<p>到这个事件前,我已经开发完了既定的MVP版本,为v 1.1,此时的RedisMate是一个完成了基础Redis功能的App。</p> +<p>而我也已经怀着激动而又紧张的心情,在想如何推广MVP版本了。</p> +<p>但是,在我准备尝试推广的那个早上,在上厕所的时候,当时正在看《人性的弱点》。刚好看到书上讲了一个推销员的案例,推销员疯狂地讲述自己的产品怎么怎么好,让作者要赶紧买。</p> +<p>因此作者就在书上写道:</p> +<blockquote> +<p>人们其实什么东西都不需要。如果我们想买点什么,早就出门去买回来了。人们真正需要的,是解决问题的方式。人类永远都面临着种种问题,永远都需要这些问题的解决方案——如果销售人员能够证明其服务或产品可以帮助人们解决问题,不用推销,我们就会主动掏钱。对消费者而言,“主动买”比“被推销”的感觉好得多。</p> +</blockquote> +<p>所以,我就在想,我现在做的这是什么啊?!我能给其他人解决什么问题呢?凭什么要选择我的,而不是别的成熟产品?</p> +<p>我就开始重新构思MVP版本,希望能够加上独特的、创新的功能,能够真实解决用户问题的功能。</p> +<h2 id="创新">创新</h2> +<h3 id="寻找创新">寻找创新</h3> +<p>如何发现问题并解决问题?可以从经济学的四种不同类型的效用来入手:</p> +<ul> +<li>场所效益:使得原本无法接触的事物变得可接触</li> +<li>形式效用:通过重新排列现有部件使某物更有价值</li> +<li>时间工具:让事情变快的工具</li> +<li>拥有权利:去除中间人</li> +</ul> +<p>在此处,我的产品RedisMate上,最简单也是最快的的创新方式就是“让事情变得更快”。</p> +<p>我先明确Redis GUI使用频率最高的功能,然后从那个功能入手,让用户能够立竿见影感受到RedisMate的便捷。</p> +<p>那么,经过自己的总结,和跟朋友的沟通,我总结出,Redis GUI工具,使用频率从高到低的功能分别是:查(甚至是搜索)、删、改、增。</p> +<p>因此,我就从搜索功能入手。</p> +<p>我把RedisMate搜索功能的所有步骤罗列出来,然后思考,有哪些步骤可以减去?如何减少步骤?</p> +<p>最终就得到了现在“快速搜索”功能!</p> +<p>当Redis GUI窗口常驻时,一般最少需要约7步操作,那么在RedisMate上最少只要3步。</p> +<p>当Redis GUI仅打开没有连接任何服务器时,一般最少需要约9步操作,但是在RedisMate上最少只要3步。</p> +<p>当Redis GUI反复搜索固定key时,一般最少需要约4步,但是在RedisMate最少仅仅需要1步。</p> +<p>所以,我在RedisMate上针对搜索场景,进行了优化,并确实在推广时,得到了不少积极反馈。</p> +<p>并且,基于上述过程,优化了RedisMate的布局和一些细微的功能,让用户能够更直观更快去操作。</p> +<h3 id="挖掘需求">挖掘需求</h3> +<p>等我后面回过神的时候,才发现我一直有这个需求。</p> +<p>经常性,我要前后端一起开发,由于是微服务,多个节点并发开发和调试,每次都要开超多窗口。</p> +<p>我经常要切换窗口时,要愣一会儿思考一下,我要切什么窗口来着&hellip;</p> +<p>所以我就一直在想,能不能省略掉这些窗口,窗口自己呼出然后切回去!</p> +<p>现在RedisMate就可以了,这就解决了我曾经的问题。</p> +<p>然后,我就又想到曾经的一个抱怨,有时候在调试时,代码有点问题,或者业务链路较长,需要联调时间有点长,或者MQ驱动的业务节点产生key有点慢等等,key在我查的时候没有出现。我就要反复调出Redis GUI然后点点点后搜索再点点点,发现不对!调一下代码,再来一遍!</p> +<p>我当时就在想,要是GUI能帮我盯着就好了。</p> +<p>所以,我设计了&quot;Search Until Found&quot;功能,在得到朋友的肯定后,将其加入到了MVP版本中 。</p> +<h2 id="总结">总结</h2> +<p>首先,我很庆幸自己这些年学习和玩了不少技术,让我能够在想去独立开发的时候,不仅能够独立完成开发,还能有多种产品形态可以选择。并且我也有意识地去读技术之外的书,让我在其它领域有那么点点基础,只可惜没有学得更多。</p> +<p>其次,我觉得对我而言,苹果生态开发是当前成本最小的方式。</p> +<p>然后,我从我熟悉的领域入手,寻找自己的痛点,验证自己的需求,得到解决方案后,向身边的目标用户寻求验证。</p> +<p>最后,花一个多月开发完MVP版本,推出来验证自己的产品。</p> +<h2 id="最后">最后</h2> +<p>感谢你看完这篇文章,希望我的经过能够给你一些参考,帮助到你。</p> +<p>本次分享的下一篇文章——产品推广方面,我正在努力写,敬请期待并关注我以获取更新。</p> +<p>如果你对我的产品RedisMate感兴趣,非常欢迎<a class="link" href="https://redismate.hunterji.com/" target="_blank" rel="noopener" + >点击此处</a>了解和下载,或者在mac app store中搜索“RedisMate“。</p> +<p>最后,要感谢各位道上兄弟的关照,给了很多支持和反馈。</p> +<p>还要特别感谢那些购买高级版的兄弟,非常感谢你们的支持!</p> + + + + 程序员如何快速验证业务需求? + https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/ + Tue, 05 Dec 2023 15:56:29 +0000 + + https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/ + <img src="https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/cover.png" alt="Featured image of post 程序员如何快速验证业务需求?" /><h2 id="前言">前言</h2> +<p>设想一个场景:</p> +<p>你作为一个程序员,听着攒劲的小曲,写着优雅的代码,加班加点终于写完了迭代中分配的模块需求。然后你跟别的同事一联调,却发现各种问题。</p> +<p>你跟同事A说:“你得在这种情况下给我数据A。“</p> +<p>同事A一脸懵逼:“这种情况需求也没写啊,在这种情况下我也没有数据A啊!“</p> +<p>然后你和同事A一起顺着业务流程,找到了负责上层模块的同事B。</p> +<p>同事B一脸懵逼:“这个迭代中我没有这个模块的开发需求啊,这种情况下我也没有数据A啊!”</p> +<p>然后你和同事A、同事B一起顺着业务流程,找到了负责上层模块的&hellip;&hellip;</p> +<p>最后,你和同事ABC&hellip;一起找到了产品,产品一脸懵逼:“那没办法,就改需求吧&hellip;&hellip;”</p> +<p>你眼看着所剩无几的迭代时间,生无可恋地掏出手机,发个微信取消了这周末的相亲&hellip;</p> +<p>不知道程序员xdm有没有经历过这样的场景,如果各位程序员xd没有经历过这样的场景,那真的是恭喜你了,且行且珍惜!从毕业后参加工作开始,我是真的经历太多了,太痛了!</p> +<p>我之后不断学习各种方法,尝试去破解这样的困局。虽然说,工作中的问题很多,但是能解决一个是一个,后面再慢慢跟各位分享其他问题中我的解决方案。</p> +<p>所以,本篇文章将分享一波关于这类问题我的解决方案。谨代表个人主观观点。</p> +<h2 id="痛点">痛点</h2> +<p>需求是用户的痛点,但是在这个场景下,是程序员的痛点&hellip;</p> +<p>那么,仔细分析一下,这个场景下的痛点,到底是什么呢?我认为是如下两点:</p> +<ul> +<li>产品给到的需求无法自洽,且场景覆盖不够</li> +<li>开发团队的技术评审和技术文档不到位</li> +<li>开发各自按照模块开发,忽略外部环境,仅仅当各自开发完成后联调时候才会组装应用,发现问题</li> +</ul> +<p>当然,可能有程序员xd会疑问:这种问题不是靠严谨的开发流程就可以避免吗?</p> +<p>理论上来说是这样的,但是以我个人经历而言,不管是大团队还是小团队,总有很多不稳定的因素,比如说迭代周期、协作流程的规范性等等。</p> +<p>特别是在创业团队,或者有些项目特别着急的时候,经常是没有足够的时间给到团队的各个角色去充分准备的,甚至为了赶时间,会精简一些环节甚至直接去掉。这种时候出现这种问题导致改需求,往往是非常可怕的,后果也往往是开发团队加班。</p> +<h2 id="解决方案">解决方案</h2> +<p>我目前发现且实践后效果较好的方案,就是“曳光弹式开发”。</p> +<h3 id="什么是曳光弹">什么是曳光弹</h3> +<p>经常关注军事的小伙伴儿,应该了解,曳光弹是一种运用于军事场景的特殊子弹。</p> +<p>我们都知道武器发射时需要瞄准,比如枪就通过照门和准星来瞄准,而坦克和装甲车以及飞机都有专用的瞄准具,非常复杂。对于轻武器来说,一般攻击距离都比较近,使用自带的机械瞄准具或者外加的光学瞄准镜都足够,但是在距离稍远的时候瞄准镜显然不够。而对于飞机和战斗机来讲,在空中飞行时飞行姿态变化多样,<strong>有时候进攻的时间很短,需要快速射击并且调整弹道,这时候就需要可以发光的曳光弹。</strong></p> +<p>曳光弹正如其名,可以发光而且指示弹道。曳光弹的结构比一般子弹更加复杂。子弹的弹壳部分和一般子弹一样,前半部分是钢心或者铅心的弹头,但是在后部有一个空腔,一般称作曳光管,里面填充着曳光剂。曳光剂的成分还比较复杂,一般来说主要成分是镁粉和铝镁合金粉,用来燃烧,除此之外还有硝酸锶。这样一来,燃烧的时候硝酸锶就会发出红光。大家平时看到的很多曳光弹还有黄光和绿光,加入钠盐就会发出黄光,而加入铜盐就会发出绿光。除此之外还在表面加入一层过氧化钡,以保证曳光剂被点燃。</p> +<p>对于机枪手来说,如果射击距离较远,自然不能选择过于精确的设计方式,也就是说不能靠瞄准具来射击。而一般的机枪主要起压制和面杀伤作用,加入曳光弹就是机枪手更加快速方便的控制弹道,随时变化攻击的方向。如果没有曳光弹的话,射手根本无法发现自己的子弹弹道,也就很难去调整弹道。毕竟枪械射击温度升高之后,弹道会有所变化,不同的弹药不同的枪管也都会改变子弹的弹道,如果仅仅依靠枪械自身的瞄准具去调整反而会适得其反,而曳光弹就很好的解决了这个问题。</p> +<h3 id="在开发中的作用">在开发中的作用</h3> +<p>理解曳光弹本身在军事中的作用后,我们再来看曳光弹式开发如何在开发中起作用。</p> +<p>开发团队在接手到产品给到的业务需求后,特别是构建一些以前从未做过的东西时,对这个产品/功能的最终成效是模糊的。</p> +<p>程序员就像坦克上的机枪手一样,在尝试在黑暗中击中目标。但是如果像文章一开始的时候,大家先埋头写自己的模块,然后再联调,最终发现问题,感觉就像一上战场,大家都朝着各自理解的方向拼命清空弹夹,击中目标的寥寥无几,最后受伤的还是自己。</p> +<p>所以,此时,就需要先使用几颗曳光弹,去击中目标,在黑暗中划出轨迹,开发团队再调整方向,对着目标集中火力。</p> +<h3 id="如何应用">如何应用</h3> +<p>非常简单!步骤如下:</p> +<ol> +<li>找出级别最高的需求</li> +<li>以完成最基础的完整功能为目标,从前端到部署,开发一个可以运行的骨架</li> +<li>最后上相关需求的测试案例</li> +</ol> +<p>这样,射出一颗曳光弹,穿透客户端、后端、数据库、运维、测试等不同层面。一旦击中目标——即符合用户需求,后续的任务便大都是搬砖的活儿,去丰富这个骨架。</p> +<p>曳光弹并不是总能击中目标的,中途若是发现未能击中目标,便可在前期以极小成本去调整曳光弹的方向,继续发射曳光弹。</p> +<p>如果你要问,什么是“最基础的完整功能”,那么可以这么说,那么举个例子:前端不要任何样式,直接能够满足比如表单功能即可,后端不用任何校验,能够处理和传递数据即可。</p> +<h2 id="总结">总结</h2> +<p>为了解决文章开头的问题,需要使用曳光弹式开发,先开发一个骨架,确认满足需求后,即可继续丰富骨架,完成产品。</p> +<p>作为团队的一员,团队的每一个角色都很重要,程序员跟产品也是需要紧密协作,互帮互助,才能使得产品更好,也使得业绩更好。</p> + + + + 如何快速入门新的编程语言和框架? + https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/ + Wed, 29 Nov 2023 21:10:40 +0000 + + https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/ + <img src="https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/cover.jpg" alt="Featured image of post 如何快速入门新的编程语言和框架?" /><h2 id="一-前言">一. 前言</h2> +<p>最近很多小伙伴儿咨询我关于学习技术方面的问题,我都一一回复了。这里也专门出一个总结,关于我个人这些年入门新的编程语言和框架的经验和技巧,来跟大家分享一下,希望能够所有帮助。</p> +<p>本次分享分为两个时期:</p> +<ul> +<li>有编程经验时期</li> +<li>新手时期</li> +</ul> +<h2 id="二-有编程经验时期">二. 有编程经验时期</h2> +<p>当我有了一定开发经验后,去学习一个新的编程语言或者框架,我会注意如下两点:</p> +<h3 id="1-寻找共性">1. 寻找共性</h3> +<h4 id="编程语言">编程语言</h4> +<p>所有的编程语言或者框架,都不是凭空产生的。当前比较流行的语言,大都是类C语言,比如我们常见的Java、Python、JavaScript、Golang等等。</p> +<p>所以,我们学习的编程语言,除了特有的特性,大都是相似的,也就是它们的共性。我们只要理解和掌握这些类C语言的共性,就可以很快地掌握一门新的编程语言。</p> +<p>那么,哪些是共性呢?比如说变量、常量、运算符、判断、循环、函数、面向对象等等。</p> +<p>我们在学习一门新的编程语言的时候,是不是必然要学习如上这些共性的知识点?</p> +<p>那么,当你已经有一个编程语言的基础后,当你在学习和写代码的时候,这些共性可以直接使用你已学会的编程语言的知识去代入。</p> +<p>举个例子,比如我已经会Go了(不过,当你先学Go的话,可能不太知道面向对象是什么/狗头),要去学一下Python。</p> +<p>我需要做的就是代入:</p> +<ul> +<li> +<p>Go语言声明变量是<code>a := 1</code>,考虑到Python没有强类型,那么就直接<code>a = 1</code>就完事了。</p> +</li> +<li> +<p>Go语言声明函数是<code>func aFunction(username String) {}</code>,那么Python不过是换了个关键词,去掉了强类型,变成<code>def aFunctino(username):</code>,当然Python如果要申明类型也是可以的,写成<code>def login(username: str):</code>。</p> +</li> +<li> +<p>其中,比如运算符大都是一样的,判断也是类似。</p> +</li> +<li> +<p>但是突然你发现,怎么Python的循环不一样,然后发现Python的循环好方便,但是其实原理还是和Go一样的。甚至,当你遇到不知道怎么处理的时候,直接来一波<code>for (i := 0; i &lt; count; i++)</code>。当然,Python没法这么写。</p> +</li> +</ul> +<p>除了我举的这些例子,还有比如正则、数据类型、数据结构等等,都可以作为共性来代入。</p> +<h4 id="框架">框架</h4> +<p>其实,框架也是如此。此处以web框架为例,比如Python的Flask、Django、FastApi,Go的Gin,Node的express,Java的SpringBoot等,都是比较熟知的框架。</p> +<p>这些Web框架,都是依托于网络的,大都是http(s)请求,甚至其他TCP请求。我们可以只在网络层面来看,它们共性就是request和response。所以在网络层面就可以理解为,后端web框架就是一个接收request和response的东西。</p> +<p>那么,看具体框架的功能上,可以抽象出路由、中间件。</p> +<p>所以,对于一个web框架,其共性为request、response、路由、中间件。</p> +<p>因此,我们不管是使用Flask、Gin还是SpringBootd等,可以使用我们已有的框架知识去代入:</p> +<ul> +<li>如何接收request,比如获取request body、headers等</li> +<li>如何使用中间件处理,比如使用中间件鉴权、传递上下文等</li> +<li>如何配置路由,让接口对外暴露</li> +<li>如何返回response,比如返回http status code、json等</li> +</ul> +<p>那么,就可以完成一个新的web框架的入门了。</p> +<h4 id="其它">其它</h4> +<p>除了编程语言和框架,其实还有很多别的都是拥有共性,可以快速入门的,比如Nginx和Apache,比如Linux发行版本,比如Kong和ApiSix,比如不同的数据库等等。甚至最近我发现,wasm并不是前端特有的,EBPF也在用wasm。</p> +<h3 id="2-看官方文档">2. 看官方文档</h3> +<p>编程语言的情况还比较少,更多是学习框架时,一定要看官方文档。</p> +<p>网上虽然有很多教程,但是毕竟是二次加工的,可能存在信息遗漏、版本更新等等问题。</p> +<p>官方文档可能比较生涩,但是它是最全最新的一手文档,会非常有助于少走弯路。</p> +<p>再次强调,一定要看官方文档!</p> +<h2 id="二-新手时期">二. 新手时期</h2> +<p>当我还是新手的时候,我其实也常常苦恼于入门——教程看了好多遍,跟着教程敲了一遍又一遍,感觉还是只会基础,自己想写点什么却不会。</p> +<p>后来,当我入门一段时间后,我返回去想一想,才想明白一些技巧。</p> +<p>所以,以我的经历,总结如下步骤:</p> +<h3 id="1-仅学基础">1. 仅学基础</h3> +<p>一开始的时候,我也是不停地啃教程,不管是文档还是视频,但是发现我还是只会基础理论知识,那些高级特性案例都敲了,却并不明白什么意思。</p> +<p>后来,我就发现,对于我而言,并不需要学太多,只要学会基础即可,甚至基础也不用太深入。</p> +<h3 id="2-自己构思写一个项目">2. 自己构思写一个项目</h3> +<p>跟着教程敲代码,效果甚微。</p> +<p>要自己构思一个项目,比如写一个TODO,写一个命令行聊天工具,写一个博客等等简单的程序。最好是自己感兴趣的领域。</p> +<p>但是,需要注意的是,一定要自己去构思如何写:</p> +<ul> +<li>你准备写一个什么程序?要达到什么效果?</li> +<li>写这个程序有哪些功能?</li> +<li>这些功能都应该怎么实现?</li> +</ul> +<p>然后,直接开始写代码!哪怕只是创建了几个文件。</p> +<p>只有自己思考的,才能深刻理解和记忆。</p> +<h3 id="3-不会就去学不懂就复制过来用">3. 不会就去学,不懂就复制过来用</h3> +<p>当然,因为仅仅学了基础,你会发现很多功能并不会,甚至比如如何读取一个文件都不会。</p> +<p>如果这时候,遇到不会的,就当场去学,哪怕是高级特性,学到把不会的这个点能做出来为止。</p> +<p>如果一个功能不懂什么意思,搞不明白,那么直接复制过来,调试到能满足功能为止,然后将这段代码和这个场景记住。</p> +<h3 id="4-多写代码迈过门槛">4. 多写代码,迈过门槛</h3> +<p>我一直觉得写代码有一个门槛,在迈过门槛前,学习会非常困难,但是,一旦迈过这个门槛,你就会发现,学起来异常轻松,你所学的技术可以(相对)融会贯通。</p> +<p>那么迈过这个门槛的方法就是:重复2和3两个步骤,多写代码,多思考。</p> +<p>是的,不断重复,然后可能某一天,当你对你学的技术恍然大悟的时候,那么其实就是迈过了那个门槛。</p> +<p>然后,之后便是更深入地学习了。</p> +<h2 id="三-总结">三. 总结</h2> +<p>一路走来,我觉得最重要的是“多写代码,多思考”。不少代码写到最后甚至都是肌肉记忆了,但是代码还是会变的,自己思考的方式和过程,自己沉淀的知识才是最宝贵的,足以支撑我能够不断学习新的技术,适应新的模式。</p> + + + + 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + Thu, 26 Oct 2023 23:51:13 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(三)——Rust与JS交互》</a>中,讲述了Rust与JS的交互,包括Rust与JS的函数相互调用,比较炸裂的功能就是使用JS调用Rust的struct,JS本身连struct都没有,居然可以调用Rust的struct,这对于Rust开发者的开发体验而言,是真的很棒!</p> +<p>基于前面的系列文章,已经足以使用Rust开发一个完整的功能了。</p> +<p>但是,在前端引入wasm文件时,还是可能存在一些问题,比如wasm文件较大,导致网页访问时间较长,用户体验较差。本篇文章将会通过多种途径来减少Rust编译wasm文件的体积,以减少前端加载wasm文件的时间。</p> +<p>曾经一段时间,我一直用<a class="link" href="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/" target="_blank" rel="noopener" + >Go开发WebAssembly</a>,其编译后的wasm文件体积还是较大的,为了减少wasm文件的体积真是煞费苦心,1xMB大小的wasm文件真的是太痛了&hellip;&hellip;但是体积优化往往都会指向一条“充满魅惑的不归路”——换成Rust开发😆!如果你已经在用Rust开发WebAssembly了,那么恭喜你,对Go而言,在wasm体积上,你已经赢在起跑线上了。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="查看体积">查看体积</h2> +<p>在真正去减少体积前,我们需要来先看一下,当前情况下体积是多少,方便后续对比前后体积。</p> +<p>查看体积的方式有多种,这里推荐几个,(Linux和MacOS)使用其一即可。</p> +<h3 id="ls">ls</h3> +<p>可以使用<code>ls -l</code>或者<code>ll</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ll pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">-rw-r--r-- <span class="m">1</span> kuari staff 23K Jul <span class="m">20</span> 21:52 pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="stat">stat</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ stat pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"><span class="m">16777222</span> <span class="m">142141572</span> -rw-r--r-- <span class="m">1</span> kuari staff <span class="m">0</span> <span class="m">23347</span> <span class="s2">&#34;Jul 20 21:52:53 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="m">4096</span> <span class="m">48</span> <span class="m">0</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="wc">wc</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23347</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>以<code>wc</code>为例,当前该wasm文件体积为<strong>23347b</strong>。</p> +<h2 id="代码层面">代码层面</h2> +<blockquote> +<p>Link-Time Optimization (LTO) 是指在程序链接时进行的一种过程间优化(interprocedural optimization)。它允许编译器在链接阶段对多个编译单元进行优化,从而提高程序的性能、可靠性和安全性。</p> +</blockquote> +<p>从代码层面优化,主要是利用LTO(Link-Time Optimization)。</p> +<h3 id="代码内">代码内</h3> +<p>在<code>Cargo.toml</code>中开启LTO:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>开启LTO虽然能够减少编译后的体积,但是也会增加编译时间。</p> +<p>LTO开启后,默认是在减少一定程度的编译体积的情况下,要确保编译的时间。如果你的需求就是更小的体积,而不是较短的时间,那么,可以通过手动指定编译等级来让LTO作出改变。</p> +<p>在代码内可以使用如下等级:</p> +<ul> +<li><code>s</code>:默认的 LTO 等级。它会进行最基本的 LTO 优化,包括内联函数、函数重写、数据重排等</li> +<li><code>z</code>:最高级的 LTO 等级。它会进行更复杂的 LTO 优化,包括死代码消除、内存分配优化、安全性优化等</li> +</ul> +<p>那么可以在<code>Cargo.toml</code>中这么配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="nx">opt-level</span> <span class="p">=</span> <span class="s1">&#39;z&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>原始的文件体积是23347b,现在编译后看一下体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19879</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>很明显是减少体积!但是,使用<code>z</code>等级并不代表一定每次体积都会比<code>s</code>小的,有时候<code>s</code>也会比<code>z</code>小,这需要视代码情况而定。</p> +<h3 id="代码外">代码外</h3> +<p>在代码外,可以使用<a class="link" href="https://github.com/WebAssembly/binaryen" target="_blank" rel="noopener" + >wasm-opt</a>来进行优化,其可以对 WebAssembly 模块进行多方面的优化,当然本篇文章中重点在体积方面(挖个坑,后面再详聊/狗头)。并且wasm-opt可以对所有符合WebAssembly规范的wasm文件进行优化,所以,就算你不是Rust写的,那也可以用其进行优化。(想想我曾经Go写的wasm,也是有多一个法子可以优化一把了&hellip;&hellip;)</p> +<p>首先,来看一下wasm-opt的基本优化参数:</p> +<ul> +<li><strong>-o</strong>:指定优化后的模块输出文件</li> +<li><strong>-O</strong>:启用默认优化,等同于<code>-Os</code>参数</li> +<li><strong>-O0</strong>:不进行任何优化</li> +<li><strong>-O1</strong>:进行一些基本的优化,例如内联函数优化和死代码消除优化</li> +<li><strong>-O2</strong>:进行更为彻底的优化,例如函数重写、数据重排、内存分配优化等</li> +<li><strong>-O3</strong>:进行最为彻底的优化,包括一些可能影响程序功能的优化</li> +<li><strong>-O4</strong>:与 <code>-O3</code> 相同,但会启用更为激进的优化</li> +<li><strong>-Os</strong>:优化目标是减小代码大小,会进行一些可能影响性能的优化</li> +<li><strong>-Oz</strong>:与 <code>-Os</code> 相同,但会启用更为激进的优化</li> +</ul> +<p>基于本篇文章主题,此处将使用<code>-Os</code>和<code>-Oz</code>两种参数,其于上述&quot;代码内&quot;的等级是对应的。</p> +<p>此处以原始wasm文件,以<code>-Oz</code>参数来执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23194</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>再以上述开启“代码内“LTO编译后的wasm文件,以wasm-opt执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19871</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>总体而言,wasm文件的体积越来越小。只是当前我这里的案例,是沿用系列文章内容的代码,没有什么实际性复杂代码,再者本身体积已经很小了,所以不会特别有效果。</p> +<h2 id="网络层面">网络层面</h2> +<p>网络层面的话,就是众所周知的在网络传输时,客户端和服务端约定相同的压缩算法,然后服务端给出时进行压缩,客户端接收时进行解压。网络层面可以对传输报文进行压缩,但不丢失信息。</p> +<p>比如大家都很熟悉的gzip压缩算法,不过,压缩算法有好几种:</p> +<ul> +<li>gzip</li> +<li>compress</li> +<li>deflate</li> +<li>br</li> +</ul> +<p>其中gzip也是压缩率最高的了,此处就以gzip为例。</p> +<p>在网络层面,将wasm文件以gzip压缩,减少其在传输时的体积。虽然减少了传输时的体积,但是浏览器在拿到压缩后的数据,需要消耗一定性能来解密。</p> +<h3 id="开启gzip">开启GZIP</h3> +<p>开启GZIP其实简单,只要前后端约定好都用gzip就行了。</p> +<p>首先,前端请求wasm文件时,需要在request header中放入浏览器支持的压缩模式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Accept-Encoding: gzip, deflate +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,服务端收到这个请求后就可以给出服务端也支持的压缩模式,并告诉浏览器服务端将会用什么压缩模式。</p> +<p>跟浏览器通信的方式就是将信息塞到respone header里面:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Content-Encoding: gzip +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样就开启GZIP了。</p> +<p>然后,就是浏览器接收到response的body和header,知道后端使用gzip压缩的,那么浏览器就会自动用gzip来解压,拿到完整的数据了。</p> +<h3 id="服务端支持">服务端支持</h3> +<p>或许你会想问,浏览器能自动解密,那服务端怎么自动加密呢?要后端写代码让文件加密吗?</p> +<p>那当然不是了,直接让http server来完成这个操作。此处以耳熟能详的Nginx为例。</p> +<p>最简单的就是一行配置开启gzip了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span></code></pre></td></tr></table> +</div> +</div><p>也可以指定gzip的一些参数,比如可以加密的类型、最小加密长度等等:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span><span class="line"><span class="cl">gzip_types text/plain application/xml; +</span></span><span class="line"><span class="cl">gzip_proxied no-cache no-store private expired auth; +</span></span><span class="line"><span class="cl">gzip_min_length 1000; +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>更多的http server配置,可以去各自官方文档查阅。</p> +<h2 id="物理层面">物理层面</h2> +<p>你可能会惊奇,什么物理层面?!</p> +<p>没错,真就是物理层面——直接对wasm文件进行gzip物理压缩!哈哈,这个方法也真是绝了,我之前在Go开发wasm时,寻找减少体积的时候发现的,如果你的wasm已经优化得穷途末路了,不妨大胆试试这个方案。😆</p> +<p>还记得上面章节“网络层面“中,有个问题就是是否需要手动压缩,那么这里就是全程手动压缩和解压缩了,哈哈。</p> +<h3 id="物理压缩">物理压缩</h3> +<p>首先,是对wasm文件进行物理层面的gzip压缩,此处先使用原始的wasm(23347b):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gzip -c pkg/hello_wasm_bg.wasm &gt; pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,看一下其体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">10403</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果卓群,从23347b减少到了10403b!</p> +<p>然后来把上述“代码层面”的优化来一遍,看一下最后的体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">9237</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果更加卓群了,从19871b减少到了9237b了!</p> +<p>所以,此处就是对wasm文件进行物理压缩并存储,然后浏览器请求时,直接请求到<code>.gz</code>文件。</p> +<h3 id="物理解压">物理解压</h3> +<p>浏览器拿到<code>.gz</code>文件后,需要物理解压。</p> +<p>这里推荐使用<code>pako</code>这个前端库,对<code>.gz</code>文件进行解压:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">gunzipWasm</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;target.gz&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">arrayBuffer</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// A fetched response might be decompressed twice on Firefox. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// See https://bugzilla.mozilla.org/show_bug.cgi?id=610679 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">buffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x1F</span> <span class="o">&amp;&amp;</span> <span class="nx">buffer</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x8B</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">buffer</span> <span class="o">=</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">buffer</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>之后就可以直接使用了。</p> +<h2 id="buff叠加">BUFF叠加</h2> +<p>此处直接将上述所有方法都用起来,直接叠加buff,来看看当前(本系列文章积累的)这个案例能减少多少体积。在“物理层面”章节中,已经累加除了“网络层面”的buff了,所以可以直接使用其结果。而“网络层面”章节中,以gzip来压缩,将gzip的压缩率以40%来估算。</p> +<p>那么最终该案例的wasm体积将在5542b,压缩率大约在77%!</p> +<p>当然,还要算上一个初始的语言buff——Rust,使用Rust本身就已经导致wasm文件体积很小了。</p> +<h2 id="总结">总结</h2> +<p>本片文章中,从代码层面、网络层面、物理层面共三个层面介绍了对wasm文件的体积优化方案,其中共有四个方案。</p> +<p>最后,当前(本系列文章积累的)该案例叠加了所有buff之后,能够减少77%的体积,真的感觉挺棒的了,哈哈。</p> +<p>希望能够对各位有所帮助。</p> + + + + 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + Tue, 27 Jun 2023 00:11:12 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(二)——DOM和类型转换》</a>中,描述了使用Rust操作DOM,并实现Rust与JS类型转换的多种方法。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>基于上一篇文章中,Rust与JS的类型转换的多种方法,本篇文章继续深入Rust与JS的交互。</p> +<p>首先,Rust与JS的类型转换,可以实现变量的传递,那么变量是要用在哪里呢?那必然是函数了!</p> +<p>所以,本篇文章来讲述一下Rust与JS的函数相互调用,基于此,可以实现大量日常功能开发。</p> +<p>并且,还将会讲述一下,如何导出Rust的struct给JS调用。</p> +<p>是的,没错,在JS调用Rust的struct!一开始看到这个功能的时候,我的脑子是有点炸裂的&hellip;&hellip;😳</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="函数的相互调用">函数的相互调用</h2> +<h3 id="js调用rust函数">JS调用Rust函数</h3> +<p>其实,在本系列文章的第一篇中,就是使用的JS调用Rust函数作为案例来演示的,这里依然以此为例,主要讲一下要点。</p> +<p>首先,声明一个Rust函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处需要注意的要点如下:</p> +<ul> +<li>引入<code>wasm_bindgen</code></li> +<li>声明一个函数,使用<code>pub</code>声明</li> +<li>在函数上使用<code>#[wasm_bindgen]</code>宏来将Rust函数导出为WebAssembly模块的函数</li> +</ul> +<p>接着,编译成wasm文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,在JS中调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server,在浏览器的控制台中可以看到<code>the result from rust is: 3</code>,表明调用成功!</p> +<h3 id="rust调用js函数">Rust调用JS函数</h3> +<p>###1 指定JS对象</p> +<p>在Rust中调用JS函数,需要进行指定JS对象,也就是说,得明确告诉Rust,这个JS函数是从JS哪儿拿来的用的。</p> +<p>主要在于下面两个方式:</p> +<ul> +<li><em><strong>js_namespace</strong></em>: 是一个可选的属性,用于指定一个JavaScript命名空间,其中包含将要在wasm模块中导出的函数。如果没有指定<code>js_namespace</code>,则所有的导出函数将被放置在全局命名空间下。</li> +<li><em><strong>js_name</strong></em>: 是另一个可选属性,它用于指定JavaScript中的函数名称。如果没有指定<code>js_name</code>,则导出函数的名称将与Rust中的函数名称相同。</li> +</ul> +<p>###2 JS原生函数</p> +<p>对于一些JS原生函数,在Rust中,需要去寻找替代方案,比如我们上一篇文章中讲的<code>console.log()</code>函数,是不是觉得好麻烦啊!</p> +<p>那么,你想直接在Rust中调用JS原生函数吗?!</p> +<p>此处,就以<code>console.log()</code>函数为例,直接在Rust中引入并调用,免去替代方案的烦恼。</p> +<p>首先,给出Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如上代码中,<code>call_js_func</code>函数,顾名思义,此处是调用了js函数,并传入参数<code>hello, javascript!</code>。</p> +<p>那么,<code>call_js_func</code>函数上方的代码,我们来一步步解析一下:</p> +<ol> +<li>第一行代码<code>#[wasm_bindgen]</code>是Rust的属性,它告诉编译器将函数导出为WebAssembly模块</li> +<li><code>extern &quot;C&quot;</code>是C语言调用约定,它告诉Rust编译器将函数导出为C语言函数</li> +<li><code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的console对象</li> +<li><code>fn log(message: &amp;str)</code>是一个Rust函数,它接受一个字符串参数,并将其打印到JavaScript中的console对象中</li> +</ol> +<p>此处与JS交互的关键是<code>js_namespace</code>。在Rust中,<code>js_namespace</code>是用于指定JavaScript命名空间的属性。在WebAssembly中,我们可以通过它将函数绑定到JavaScript中的对象上。</p> +<p>在上述代码中,<code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的<code>console</code>对象。这意味着在JS中使用<code>console.log()</code>函数来调用Rust中的<code>log()</code>函数。</p> +<p>因此,类似的原生函数,都可以使用该方法来实现调用。</p> +<p>最后,我们在JS中调用下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">call_js_func</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">call_js_func</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以在浏览器的控制台中看到<code>hello, javascript!</code>。妙啊!</p> +<p>其实对于<code>console.log()</code>而言,还有另一种调用方式,那就是使用<code>js_namespace</code>和<code>js_name</code>同时指定。</p> +<p>或许,你会问,这有什么不同吗?是的,这有些不同。</p> +<p>不知道你是否发现,当前这个案例中,指定了<code>js_namespace</code>为<code>console</code>,但是真实执行的函数是<code>log()</code>,那么这个<code>log</code>函数的指定,其实是体现在Rust中同样的函数名<code>log</code>。也就是说,该案例的<code>log()</code>就是<code>console.log()</code>中的<code>log()</code>。</p> +<p>我们来换个名字看看,将原来的<code>log()</code>换成<code>log2()</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log2</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log2</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译后去控制台看看,就会看到报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">TypeError: console.log2 is not a function +</span></span></code></pre></td></tr></table> +</div> +</div><p>因此,当我们使用<code>js_namespace</code>和<code>js_name</code>结合的方式,在此处是可以进行自定义函数名的。</p> +<p>看一下Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console, js_name = log)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log_str</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log_str</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,重新定义了一个函数<code>log_str</code>,但是其指定了<code>js_namespace = console</code>和<code>js_name = log</code>,那么此处,就可以使用自定义的函数名。</p> +<p>直接编译后,在控制台看一下,可以直接看到正常输出:<code>hello, javascript!</code>。</p> +<p>总结一下,如果没有指定<code>js_name</code>,则 Rust 函数名称将用作 JS 函数名称。</p> +<p>###3 自定义JS函数</p> +<p>在一定场景下,需要使用Rust调用JS函数,比如对于一些对于JS而言更有优势的场景——用JS操作DOM,用Rust计算。</p> +<p>首先,创建一个文件<code>index.js</code>,写入一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">addIt</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当前的文件结构关系如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── Cargo.lock +</span></span><span class="line"><span class="cl">├── Cargo.toml +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── index.html +</span></span><span class="line"><span class="cl">├── index.js +</span></span><span class="line"><span class="cl">├── pkg +</span></span><span class="line"><span class="cl">│   ├── README.md +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.js +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">│   └── package.json +</span></span><span class="line"><span class="cl">├── src +</span></span><span class="line"><span class="cl">│   └── lib.rs +</span></span><span class="line"><span class="cl">└── target +</span></span><span class="line"><span class="cl"> ├── CACHEDIR.TAG +</span></span><span class="line"><span class="cl"> ├── debug +</span></span><span class="line"><span class="cl"> ├── release +</span></span><span class="line"><span class="cl"> └── wasm32-unknown-unknown +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>index.js</code>和<code>lib.rs</code>,以及<code>hello_wasm_bg.wasm</code>都是不在同一级别的,<code>index.js</code>都在其它两个文件的上一级。记住这个机构关系!</p> +<p>然后,在<code>lib.rs</code>中,指定函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl">##<span class="p">.</span><span class="o">/</span><span class="n">index</span><span class="p">.</span><span class="n">js</span><span class="s">&#34;)] +</span></span></span><span class="line"><span class="cl"><span class="s">extern &#34;</span><span class="n">C</span><span class="s">&#34; { +</span></span></span><span class="line"><span class="cl"><span class="s"> fn addIt(m: i32, n: i32) -&gt; i32; +</span></span></span><span class="line"><span class="cl"><span class="s">} +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>raw_module = &quot;../index.js&quot;</code>的意思是,指定对应的<code>index.js</code>文件,大家应该清楚,此处指定的是刚刚创建的<code>index.js</code>。<code>raw_module</code>的作用就是用来指定js文件的。</p> +<p>这段代码在前端,可以等同于:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addIt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;../index.js&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样在前端都不用引入了,直接在Rust中引入了,感觉还有点奇妙的。</p> +<p>接着,在Rust调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addIt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在前端调用,编译后,在浏览器的控制台中可以看到输出结果了!</p> +<p>总结一下,这里有几个注意点:</p> +<ol> +<li>JS的函数必须要export,否则将无法调用;</li> +<li><code>raw_module</code>只能用来指定相对路径,并且,大家可以在浏览器的控制台中注意到,此处的<code>../</code>的相对路径,其实是以wasm文件而言的相对路径,这里一定要注意呀!</li> +</ol> +<h2 id="js调用rust的struct">JS调用Rust的struct</h2> +<p>现在,来点炸裂的,JS调用Rust的struct?!</p> +<p>JS中连struct都没有,这玩意儿导出来会是什么样,得怎么在JS中调用呢?!</p> +<p>首先,定义一个struct,并且声明几个方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(constructor)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_user</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;name is : </span><span class="si">{}</span><span class="s">, age is : </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="p">).</span><span class="n">as_str</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">set_age</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">age</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,声明了一个struct名为<code>User</code>,包含<code>name</code>和<code>age</code>两个字段,并声明了<code>new</code>、<code>print_user</code>和<code>set_age</code>方法。</p> +<p>其中还有一个未见过的<code>#[wasm_bindgen(constructor)]</code>,<code>constructor</code>用于指示被绑定的函数实际上应该转换为调用 JavaScript 中的 new 运算符。或许你还不太清晰,继续看下去,你就会明白了。</p> +<p>接着,在JS中调用这个struct,和其方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">addIt2</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">User</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;kuari&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">set_age</span><span class="p">(</span><span class="mi">21</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">print_user</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到,这里的用法就很熟悉了!</p> +<p>大概想一下,在Rust中要如何调用?也就是直接new一个——<code>User::new('kuari', 20)</code>。</p> +<p>此处在JS中,也是如此,先new一个!</p> +<p>然后很自然地调用struct的方法。</p> +<p>编译后,打开浏览器,可以在控制台看到输出:<code>name is : kuari, age is : 21</code>。</p> +<p>其实,或许大家会很好奇,起码我是非常好奇的,Rust的struct在JS中到底是一个怎样的存在呢?</p> +<p>这里直接添加一个<code>console.log(user)</code>,就可以在输出看到。那么到底在JS中是一个怎样的存在呢?请各位动手打印一下看看吧!:P</p> +<h2 id="总结">总结</h2> +<p>本篇文章中,主要讲述了Rust与JS的交互,体现在Rust与JS的相互调用,这是建立在<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >上一篇文章</a>中类型转换的基础上的。</p> +<p>Rust与JS的函数相互调用的学习成本还是较大的,而且对比Go写wasm,Rust的颗粒度是非常细的,几乎可以说是随心所欲了。</p> +<p>比较炸裂的就是Rust的struct导出给JS用,这对于Rust与JS的交互而言,还是非常棒的体验。</p> + + + + 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + Sun, 18 Jun 2023 18:18:31 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/72" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(一)——快速开始》</a>中,描述了如何创建项目和快速生成wasm并在前端中使用,迈出了整花活儿的第一步。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +<li>web-sys 0.3.64</li> +</ul> +<h2 id="dom">DOM</h2> +<h3 id="配置依赖">配置依赖</h3> +<p>要操作DOM,需要引入新的依赖<code>web-sys</code>,因此,可以配置<code>Cargo.toml</code>中依赖如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>你或许会好奇,这个<code>features</code>是什么,讲真,我一开始很好奇,又没看到什么特别的说明,试错才发现,原来是要手动引入功能依赖&hellip;比如说,当你需要在Rust中使用JS的<code>console</code>,那么你需要在<code>features</code>中加入<code>console</code>。</p> +<h3 id="获取document">获取Document</h3> +<p>在Rust中使用<code>Document</code>,我们需要按照上一步的说明,添加<code>features</code>。那么这里有一个依赖关系,首先在Rust中获取<code>window</code>,然后再获取<code>document</code>。</p> +<p>因此,添加<code>features</code>后如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后在<code>lib.rs</code>中创建一个函数,用来调用<code>document</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>那么,现在就是在Rust中解锁了<code>document</code>,就可以在前端为所欲为了!</p> +<h3 id="操作element">操作Element</h3> +<p>那么开始操作一波,首先得获取到<code>Element</code>&hellip;&hellip;</p> +<p>是的,你没有想错,继续来添加<code>features</code>吧,此处要添加一个<code>Element</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>ok,那么继续。此处设定函数传入两个参数<code>selector</code>和<code>message</code>,然后通过<code>selector</code>获取element,更新值为<code>message</code>参数的值。完整函数如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">(</span><span class="n">selector</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">document</span><span class="p">.</span><span class="n">query_selector</span><span class="p">(</span><span class="n">selector</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load element&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">element</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">element</span><span class="p">.</span><span class="n">set_inner_html</span><span class="p">(</span><span class="n">message</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">panic!</span><span class="p">(</span><span class="s">&#34;Failed to set inner html&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编译">编译</h3> +<p>将写完的Rust项目编译成wasm:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在html中调用">在html中调用</h3> +<p>基于上一篇文章的项目中的html,此处添加一个<code>div</code>,id为<code>message</code>,添加调用wasm的<code>update_message</code>函数,代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;message&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">update_message</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> <span class="c1">// 引入update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">update_message</span><span class="p">(</span><span class="s1">&#39;#message&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;h1&gt;Hello, Rust!&lt;/h1&gt;&#39;</span><span class="p">);</span> <span class="c1">// 调用update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在浏览器验证">在浏览器验证</h3> +<p>启动一个http server,然后在浏览器查看,可以看到在页面上出现一个<code>h1</code>标签的<code>Hello, Rust!</code>。</p> +<h3 id="发现更多方法">发现更多方法</h3> +<p>按照文章来写的过程中,你应该会发现一个问题——怎么这些方法没有补全?!</p> +<p>是的,没错的,(至少我发现)当前<code>web-sys</code>并没有补全,所以只能结合开发者优秀的前端技能和丰富的<a class="link" href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/" target="_blank" rel="noopener" + >官方文档</a>来开发了。</p> +<h2 id="rust与js的类型相互转换">Rust与JS的类型相互转换</h2> +<p>对于wasm而言,性能固然是提升的,但是类型转换一直是个问题。当大量数据需要在wasm/js中进行类型转换时,这对性能来说,真的是个灾难。之前在使用go开发wasm时,就遇到过这样的问题,需要用官方的方法来进行手动类型转换,然而wasm处理的是一个很大的数据量&hellip;&hellip;</p> +<p>不过好在Rust的类型支持真的挺丰富的!</p> +<h3 id="基础类型">基础类型</h3> +<p>基础类型挺简单的,而且Rust的范性也很好地支持了很多类型。如下是基础类型映射表:</p> +<table> +<thead> +<tr> +<th>Rust类型</th> +<th>JavaScript类型</th> +</tr> +</thead> +<tbody> +<tr> +<td>i8</td> +<td>number</td> +</tr> +<tr> +<td>i16</td> +<td>number</td> +</tr> +<tr> +<td>i32</td> +<td>number</td> +</tr> +<tr> +<td>i64</td> +<td>BigInt</td> +</tr> +<tr> +<td>u8</td> +<td>number</td> +</tr> +<tr> +<td>u16</td> +<td>number</td> +</tr> +<tr> +<td>u32</td> +<td>number</td> +</tr> +<tr> +<td>u64</td> +<td>BigInt</td> +</tr> +<tr> +<td>f32</td> +<td>number</td> +</tr> +<tr> +<td>f64</td> +<td>number</td> +</tr> +<tr> +<td>bool</td> +<td>boolean</td> +</tr> +<tr> +<td>char</td> +<td>string</td> +</tr> +<tr> +<td>&amp;str</td> +<td>string</td> +</tr> +<tr> +<td>String</td> +<td>string</td> +</tr> +<tr> +<td>&amp;[T] 例如:&amp;[u8]</td> +<td>[T] 例如:Uint8Array</td> +</tr> +<tr> +<td>Vec<T></td> +<td>Array</td> +</tr> +</tbody> +</table> +<h3 id="基础类型转换示例">基础类型转换示例</h3> +<p>在<code>lib.rs</code>文件中,创建一个函数,挑选几个类型作为参数传入,然后将其读取并打印:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到该函数传入了JS的<code>number</code>、<code>boolean</code>、<code>Uint8Array</code>和<code>Array</code>四个类型的参数。</p> +<p>然后编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在前端中引入函数并调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_values</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_values</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">,</span> <span class="nx">jsBoolean</span><span class="p">,</span> <span class="nx">jsUint8Array</span><span class="p">,</span> <span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server并打开浏览器,在控制台可以看到&hellip;看不到?!</p> +<p>是的,没错,Rust的<code>println!</code>只会将打印的内容发送到Rust的标准输出流,而不是前端的控制台。如果想在控制台中打印,那么需要调用JS的<code>console</code>了。</p> +<p>使用新的功能,第一步就是添加<code>features</code>,<code>Cargo.toml</code>中添加<code>console</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在Rust中调用<code>console.log()</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="s">&#34;Hello, Rust!&#34;</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处将其封装成一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">console_log</span><span class="p">(</span><span class="n">message</span>: <span class="nb">String</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,将示例函数的<code>println</code>改成<code>console_log()</code>和<code>format!</code>,函数代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,编译之后,打开浏览器,就可以在控制台看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">js number: 10 +</span></span><span class="line"><span class="cl">js boolean: true +</span></span><span class="line"><span class="cl">js Uint8Array item: 1 +</span></span><span class="line"><span class="cl">js Uint8Array item: 2 +</span></span><span class="line"><span class="cl">js Uint8Array item: 3 +</span></span><span class="line"><span class="cl">js number array item: 30 +</span></span><span class="line"><span class="cl">js number array item: 40 +</span></span><span class="line"><span class="cl">js number array item: 50 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="通用类型">通用类型</h3> +<p>Rust中提供了一个通用的类型——JsValue,可以作为任何JS类型。</p> +<p>这里给一个简单的案例,设置一个函数,使用<code>JsValue</code>作为参数传入,并打印。</p> +<p>创建函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_js_value</span><span class="p">(</span><span class="n">val</span>: <span class="nc">JsValue</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">val</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译成wasm文件。</p> +<p>在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_js_value</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsBoolean</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsUint8Array</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在html中,传入了不同类型的参数,但是在浏览器的控制台中可以看到,将所有不同类型的参数都打印出来了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">JsValue(10) +</span></span><span class="line"><span class="cl">JsValue(true) +</span></span><span class="line"><span class="cl">JsValue(Uint8Array) +</span></span><span class="line"><span class="cl">JsValue([30, 40, 50]) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="result">Result</h3> +<p><code>Result</code>在Rust中是一个很重要的存在,经常写Rust的话,也不想在写WebAssembly时改变开发习惯。</p> +<p>其实对于JS而言,<code>Result</code>可以直接在<code>catch</code>中捕获到,只是说,这里我们需要定义好参数类型。</p> +<p>####1 使用Result返回报错</p> +<p>首先来一个只返回报错的场景:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">only_return_error_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>这里返回类型是<code>Result</code>,但是仅仅返回了一个错误。值得注意的是,这里的报错使用的类型是<code>JsError</code>,当然,这里也可以使用<code>JsValue</code>。</p> +<p>然后在html调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">only_return_error_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;1 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;100 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>那么,在浏览器的控制台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">100 is ok +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 使用Result返回正常值和错误</p> +<p>那么,如果想既返回正常值,也想返回错误呢?Rust返回一个<code>Result</code>是没有问题,那么JS怎么解析呢?</p> +<p>直接上Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_all_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>该函数,获取到参数后,如果满足条件,加10后返回,否则报错。</p> +<p>那么看看html中如何调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_all_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,正常获取就行了&hellip;&hellip;/捂脸哭</p> +<p>这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>最后,就是在浏览器的控制台中看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">get 110 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="直接引入js类型">直接引入JS类型</h3> +<p>如果你想更直接一点,那么可以直接引入JS类型!这里主要是利用<code>js-sys</code>这个依赖,可以在<a class="link" href="https://docs.rs/js-sys/latest/js_sys/" target="_blank" rel="noopener" + >官方文档</a>上看到很多JS的类型和函数,直接引入即可使用。当然,一定场景下,直接引入的类型,是需要手动转换类型的。</p> +<p>####1 配置依赖</p> +<p>在<code>Cargo.toml</code>中添加<code>js-sys</code>依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="nx">js-sys</span> <span class="p">=</span> <span class="s2">&#34;0.3.61&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 Uint8Array</p> +<p>首先以<code>Uint8Array</code>举例,在<code>lib.rs</code>头部引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Uint8Array</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后创建一个函数,参数和返回都是<code>Uint8Array</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_uint8_array</span><span class="p">(</span><span class="n">js_arr</span>: <span class="nc">Uint8Array</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Uint8Array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// new Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">new_with_length</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Uint8Array -&gt; vec +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">)</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">to_vec</span><span class="p">().</span><span class="n">iter</span><span class="p">().</span><span class="n">enumerate</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Avoid type conversion +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">js_arr</span><span class="p">.</span><span class="n">length</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">get_index</span><span class="p">(</span><span class="n">index</span><span class="p">)));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// vec -&gt; Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">arr2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">from</span><span class="p">(</span><span class="n">vec</span><span class="p">.</span><span class="n">as_slice</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">arr2</span><span class="p">.</span><span class="n">clone</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">arr</span><span class="p">.</span><span class="n">set_index</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>忽略该函数中无意义的逻辑和<code>arr</code>变量的警告,只是为了演示用法。</p> +</blockquote> +<p>可以在代码中看到,直接引入的<code>Uint8Array</code>有自己的方法,一定场景下,需要转换类型,但是最好避免进行类型转换,而直接使用其自带的方法。</p> +<p>这里可以简要总结下,就是最好一定场景内全部使用直接引入的JS类型,或者直接全部使用Rust类型来代替JS类型,两者都存在场景下,手动转换类型是件很糟糕的事。</p> +<p>####3 Date</p> +<p><code>Date</code>类型,在上面的篇章中都没有提及,这里可以直接引入JS的<code>Date</code>类型来使用。</p> +<p>首先是引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Date</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,创建一个函数,返回时间戳:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_time</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Date</span>::<span class="n">new_0</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">date</span><span class="p">.</span><span class="n">get_time</span><span class="p">()</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_time</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;current time: &#39;</span><span class="p">,</span> <span class="nx">return_time</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在浏览器的控制台中,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">current time: 1686979932833 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="总结">总结</h2> +<p>本文中,主要讲述了如何使用Rust来实现DOM操作,读者可以根据方法自己去找到合适的方法,来实现自己的场景。其次,还讲述了Rust与JS的类型转换,从基础的各自类型的映射,到Rust独有的<code>Result</code>,到直接引入JS类型。当然这里需要注意的是,直接引入JS类型和Rust的基础类型映射JS类型这两种方法尽量不要混用,混用会导致需要手动类型转换,造成性能损耗。</p> +<p>至此,又向Rust和WebAssembly整花活儿迈进了一步~😼</p> + + + + 使用Rust和WebAssembly整花活儿(一)——快速开始 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + Wed, 14 Jun 2023 17:32:28 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(一)——快速开始" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<p>之前写过一篇文章,是关于如何使用golang来开发WebAssembly的——<a class="link" href="https://blog.hunterji.com/p/webassembly--%e6%9c%aa%e6%9d%a5%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e7%9a%84%e5%bf%85%e5%a4%87%e6%8a%80%e8%83%bd" target="_blank" rel="noopener" + >WebAssembly:未来前端开发的必备技能</a>。</p> +<p>Rust和Go都可以用来开发WebAssembly,但它们有各自的优势和劣势。</p> +<p>Rust的优点:</p> +<ul> +<li>更快的性能和更小的二进制文件</li> +<li>更好的内存安全性</li> +</ul> +<p>Go的优点:</p> +<ul> +<li>更容易上手和学习</li> +<li>更好的生态系统和社区支持</li> +</ul> +<p>综合来说,如果你更注重性能和内存安全性,那么Rust可能是更好的选择。而如果你更注重开发效率和易用性,那么Go可能更适合你。当然,实际情况还需要根据具体的项目需求和团队情况来选择。</p> +<p>因为一些工作需求,最近整了些rust的花活儿,这里系统地记录一下。当你遇到更注重性能和内存安全性的场景,希望这能有帮助。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="创建项目并添加依赖">创建项目并添加依赖</h2> +<p>此处默认已经安装Rust,需要安装的小伙伴儿可以参考<a class="link" href="https://www.rust-lang.org/tools/install" target="_blank" rel="noopener" + >官网</a>。</p> +<p>使用Cargo创建一个名为<code>hello-wasm</code>的项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo new --lib hello-wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>进入项目,打开文件<code>Cargo.toml</code>,添加依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="更新librs">更新lib.rs</h2> +<p>默认创建项目中,存在一个名为<code>lib.rs</code>的文件,将内容全部替换成:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="编译">编译</h2> +<p>至此,我们创建了一个最简单的功能——一个返回两个整数相加结果的函数。</p> +<p>然后我们可以进行编译了,编译之前需要安装一个工具<code>wasm-pack</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo install wasm-pack +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>编译完成之后,将会多出来一个<code>pkg</code>文件夹,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pkg +</span></span><span class="line"><span class="cl">├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">├── hello_wasm.js +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">└── package.json +</span></span></code></pre></td></tr></table> +</div> +</div><p>虽然文件很多,但是首先我们看到了我们所需要的wasm文件,并且,根据go的wasm引入方式,这里我们或许会需要用到js文件。</p> +<h2 id="前端引入">前端引入</h2> +<p>为了方便最快校验,直接在<code>hello-wasm</code>项目中创建<code>index.html</code>文件,来进行前端引入。</p> +<h3 id="创建indexhtml">创建index.html</h3> +<p>那么,首先,创建<code>index.html</code>文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错!这是一场标准的开局!😼</p> +<h3 id="引入wasm">引入WASM</h3> +<p>其实不同于go语言的wasm引入方式,Rust更希望直接引入js文件,而不是让开发者手动引入wasm文件。</p> +<p>这里使用js引入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="完整代码">完整代码</h3> +<p>完整的html代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="验证">验证</h2> +<p>这里可以快速起一个http server,这里我选择使用<code>http-server</code>,也可以使用<code>python3 -m http.server</code>这样的方式,看怎么各自的使用习惯。</p> +<p>那么,启动http server:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">http-server +</span></span></code></pre></td></tr></table> +</div> +</div><p>打开浏览器,访问<code>http://localhost:8080</code>,打开调试器,即可看到输出<code>the result from rust is: 3</code>,这就意味着迈出了整花活儿的第一步!</p> +<p><img src="https://user-images.githubusercontent.com/25321169/245747861-4fb71bd2-9f90-41b3-ada8-6e1d930044d4.png" + + + + loading="lazy" + + alt="rust_wasm" + + +></p> +<h2 id="常见问题">常见问题</h2> +<h3 id="前端报响应类型错误">前端报响应类型错误</h3> +<p>详细报错如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Failed to load module script: The server responded with a non-JavaScript MIME type of &#34;application/wasm&#34;. Strict MIME type checking is enforced for module scripts per HTML spec. +</span></span></code></pre></td></tr></table> +</div> +</div><p>当引入WebAssembly生成的js文件时,可能会遇到这个报错。报错乍一看是http server的响应问题,或者搜索时候,也会有帖子说这是一个response问题。</p> +<p>实际上,当按照这个文章一步步操作时是不会有这个问题的,是因为本文的编译参数是直接解决了这个问题的。当我自己摸索的时候,解决这个问题真的是看到人都麻了……</p> +<p>关键在于编译命令的参数:<code>--target</code>。</p> +<p>当没有设置这个参数时,默认的参数其实是<code>--target bundler</code>,其是编译成给webpack之类的脚手架使用的。因此这里使用<code>—target web</code>,则是使其编译成可直接在web中使用。</p> +<p>相关参数如下:</p> +<ul> +<li><strong>bundler</strong>:编译成给webpack之类的脚手架使用</li> +<li><strong>web</strong>:编译成web可直接使用</li> +<li><strong>nodejs</strong>:编译成可通过require来加载的node模块</li> +<li><strong>deno</strong>:编译成可通过import加载的deno模块</li> +<li><strong>no-modules</strong>:跟web类似,但是更旧,且不能使用es模块</li> +</ul> +<h3 id="直接引入wasm文件">直接引入wasm文件</h3> +<p>若此时尝试直接引入wasm文件,而不是使用本文所述的方式,那么你会发现,也是可行的!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;./pkg/hello_wasm_bg.wasm&#34;</span><span class="p">),</span> <span class="p">{}).</span><span class="nx">then</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;the result from rust is: &#39;</span><span class="p">,</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,当前是可行的,但是当引入了一些别的比如dom之类的,就坏起来了……</p> +<h3 id="更新的wasm引入方式">更新的wasm引入方式</h3> +<p>上一问题中,且不说是否可以直接引入wasm文件,这里仅说一下,<code>instantiateStreaming</code>这个方法。这是一个更新的方法,无需转成<code>arrayBuffer</code>,这也是摸索Rust整活儿时候发现的。如果在别的语言引入wasm,请使用这个<a class="link" href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming" target="_blank" rel="noopener" + >更新的方法</a>吧。</p> + + + + 原来浏览器原生支持JS复制到剪切板 + https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + Wed, 15 Mar 2023 22:39:35 +0000 + + https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + <h2 id="第三方库的痛苦">第三方库的痛苦</h2> +<p>在日常的前端开发中,经常需要将一些数据从网页上复制到剪切板中。而实现复制功能,第一时间想到的就是引入第三方库。</p> +<p>曾经过多不少第三方的剪切板的库,是真的很繁琐,又是创建对象,又是绑定DOM,头都要炸了,就个简单的复制功能,第三方库换来换去地测试&hellip;&hellip;</p> +<p>后来看到了vueuse可以直接用,突然觉得,哇!真棒!</p> +<p>直到有一天,搜到了Clipboard api&hellip;&hellip;</p> +<h2 id="原生支持">原生支持</h2> +<p><em><strong>官方文档</strong></em>:https://developer.mozilla.org/en-US/docs/Web/API/Clipboard</p> +<p>不管是读,还是写,统统搞定!而且都还是异步方法。</p> +<p>比如复制文本到剪切板:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">writeText</span><span class="p">(</span><span class="s2">&#34;&lt;empty clipboard&gt;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>复制canvas到剪切板:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">copyCanvasContentsToClipboard</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">onDone</span><span class="p">,</span> <span class="nx">onError</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toBlob</span><span class="p">((</span><span class="nx">blob</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">[</span><span class="k">new</span> <span class="nx">ClipboardItem</span><span class="p">({</span> <span class="p">[</span><span class="nx">blob</span><span class="p">.</span><span class="nx">type</span><span class="p">]</span><span class="o">:</span> <span class="nx">blob</span> <span class="p">})];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">data</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onDone</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onError</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>读取剪切板的文本:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">txt</span> <span class="o">=</span> <span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">readText</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + 浏览器上ts实现前端直传minio + https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/ + Fri, 17 Feb 2023 13:31:56 +0000 + + https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/ + <h2 id="前言">前言</h2> +<p>前端从后端获取到sts,然后直接minio,极大减少服务端的压力。</p> +<p>当然,肯定会这种疑问,为什么不在后端生成临时签名url,给前端上传/下载呢?问就是业务需要 /狗头&hellip;&hellip;</p> +<h2 id="环境">环境</h2> +<ul> +<li><em>minio</em>: ^7.0.32</li> +<li><em>typescript</em>: 4.9.5</li> +</ul> +<h2 id="浏览器上的坑">浏览器上的坑</h2> +<p>为什么标题上要强调“浏览器”呢?</p> +<p>这是因为官方的minio.js,感觉当前版本并没有考虑浏览器的使用场景,无法直接在浏览器上直传。</p> +<p>所以这里就是推荐一个折中的方案,可以在前端直传。</p> +<p>当然了,如果业务允许,当前版本请直接在后端生成临时签名url吧!</p> +<h2 id="minio直传代码实现">minio直传代码实现</h2> +<h3 id="安装miniojs">安装minio.js</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pnpm add -D minio +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="生成client">生成client</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">Minio</span> <span class="nx">from</span> <span class="s1">&#39;minio&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">minioClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Minio</span><span class="p">.</span><span class="nx">Client</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">region</span><span class="o">:</span> <span class="s1">&#39;cn-north-1&#39;</span><span class="p">,</span> <span class="c1">// region字段,极其有必要加上!我在sts场景,不加region字段就报错权限不够/心累... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">endPoint</span><span class="o">:</span> <span class="s1">&#39;192.168.1.1&#39;</span><span class="p">,</span> <span class="c1">// minio的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">port</span><span class="o">:</span> <span class="mi">9000</span><span class="p">,</span> <span class="c1">// minio端口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">useSSL</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 是否使用ssl +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">accessKey</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">secretKey</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sessionToken</span><span class="p">,</span> <span class="c1">// 可选字段,当为sts时,加入此字段 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="putobject直传方案">putObject直传方案</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">putObject</span><span class="p">(</span><span class="nx">bucketName</span><span class="p">,</span> <span class="nx">objectName</span><span class="p">,</span> <span class="nx">stream</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>官方提供的<code>putObject</code>方法,必填这三个字段,前两个很好理解,主要是第三个<code>stream</code>,需要详细看一下。</p> +<p><code>stream</code>的类型是:<code>string | internal.Readable | Buffer</code></p> +<p><code>string</code>很好搞定,直接<code>putObject(bucketName, 'hello.txt', 'hello,world!')</code>这样子上传文本文件。</p> +<p>但是另外两个类型都是nodejs的啊&hellip;啊这&hellip;(也许是我错了,有大佬能够解决的请务必直接告诉我&hellip;)</p> +<h3 id="折中方案">折中方案</h3> +<p>那么折中的方案就是由前端生成临时签名url,再由前端进行上传 /哭。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">minioClient</span><span class="p">.</span><span class="nx">presignedPutObject</span><span class="p">(</span><span class="nx">bucketName</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后利用http请求url进行上传即可。虽然比较曲折,但是目前相对比较好的前端解决方案。</p> +<h2 id="http请求url上传">http请求url上传</h2> +<p>此处介绍下http请求上传的相关操作。</p> +<h3 id="请求上传">请求上传</h3> +<p>请求上传可以有三种方案。</p> +<h4 id="xmlhttprequest">XMLHttpRequest</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">xhrUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="fetch">Fetch</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fetchUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;PUT&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">body</span><span class="o">:</span> <span class="nx">file</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="axios">Axios</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">axiosUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">file</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="nx">file</span><span class="p">.</span><span class="nx">type</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="promise">Promise</h3> +<p>此处可以封装一下请求,叠加<code>promise</code>buff,此处以<code>XMLHttpRequest</code>为例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">uploadRequest</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">response</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> +</span></span><span class="line"><span class="cl"> <span class="nx">reject</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="事件响应">事件响应</h3> +<p>要完成上传,怎么能没有响应事件呢。</p> +<p>此处的事件包括:</p> +<ul> +<li>上传进度</li> +<li>上传完成</li> +<li>上传失败</li> +</ul> +<p>请求代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">uploadRequest</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">upload</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;progress&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploadProgress</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="p">((</span><span class="nx">e</span><span class="p">.</span><span class="nx">loaded</span> <span class="o">/</span> <span class="nx">e</span><span class="p">.</span><span class="nx">total</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">).</span><span class="nx">toFixed</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> <span class="c1">// 更新进度,此处不保留小数点 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploaded</span><span class="p">()</span> <span class="c1">// 响应上传完成事件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploadErr</span><span class="p">((</span><span class="nx">error</span> <span class="nx">as</span> <span class="nb">Error</span><span class="p">).</span><span class="nx">message</span><span class="p">)</span> <span class="c1">// 响应上传错误,此处ts处理error +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> <span class="nx">onUploadErr</span><span class="p">(</span><span class="sb">`http code is </span><span class="si">${</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> <span class="p">}</span> <span class="c1">// 响应上传错误 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="取消上传">取消上传</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">xhr</span><span class="p">.</span><span class="nx">abort</span><span class="p">()</span> <span class="c1">// 取消上传 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="性能优化">性能优化</h2> +<ul> +<li>对于大文件,或者说需要后台运行的上传任务,可以使用web worker来跑</li> +<li>对于大文件,可以使用切片的方式提高并发,切片的方式也可以实现断点续传,只是需要注意文件切片的顺序和唯一id</li> +</ul> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/minio/minio-js" target="_blank" rel="noopener" + >github.com/minio/minio-js</a></li> +</ul> + + + + WebAssembly -- 未来前端开发的必备技能 + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + Thu, 09 Feb 2023 13:15:34 +0000 + + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + <img src="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/cover.jpg" alt="Featured image of post WebAssembly -- 未来前端开发的必备技能" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<h2 id="快速上手">快速上手</h2> +<h3 id="用go写一个hello-world">用go写一个hello world</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello, WebAssembly!&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="将go文件编译成wasm文件">将go文件编译成wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">GOOS</span><span class="o">=</span>js <span class="nv">GOARCH</span><span class="o">=</span>wasm go build -o static/main.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="拷贝出wasm_execjs">拷贝出wasm_exec.js</h3> +<p>该文件为go的wasm的js支持文件</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cp <span class="s2">&#34;</span><span class="k">$(</span>go env GOROOT<span class="k">)</span><span class="s2">/misc/wasm/wasm_exec.js&#34;</span> static +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="html文件调用wasm文件">html文件调用wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证调用">验证调用</h3> +<p>浏览器加载html文件,f12打开控制台,可以看到wasm的打印消息。</p> +<h2 id="go与js的类型转换">go与js的类型转换</h2> +<h3 id="类型映射">类型映射</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><p>如上为官方给出的go与js的类型映射表。</p> +<p>比如在go中调用js函数,参数为<code>array</code>,那么就可以直接将go的<code>[]interface{}</code>类型的变量作为参数使用。</p> +<h3 id="函数转换数组">函数转换数组</h3> +<p><code>syscall/js</code>提供了两个函数:</p> +<ul> +<li>CopyBytesToGo:<code>func CopyBytesToGo(dst []byte, src Value) int</code></li> +<li>CopyBytesToJS:<code>func CopyBytesToJS(dst Value, src []byte) int</code></li> +</ul> +<p>两者对于go而言,类型都是<code>[]byte</code>,但是对于js而言,需要<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型,否则就会报错。</p> +<p>那么,如何在go中生成一个<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型的变量呢?官方的类型映射表也没有啊&hellip;那么就看下一步。</p> +<h3 id="其余类型">其余类型</h3> +<p>对于非官方类型映射表内的类型,和官方提供的两个数据类型转换之外的类型,可以通过一种通用的方式来生成,以上一步的<code>Uint8Array</code>为例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">length</span><span class="o">&gt;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>实际使用案例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// goData []byte{...} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">jsData</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="nx">len</span><span class="p">(</span><span class="nx">goData</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">CopyBytesToJS</span><span class="p">(</span><span class="nx">jsData</span><span class="p">,</span> <span class="nx">goData</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>那么,比如js中的<code>Date</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">dateConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Date&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">dateConstructor</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s2">&#34;2020-10-01&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="极端情况">极端情况</h3> +<p>好吧,还有最后一个方案,如果遇到极端情况,上述方案都无法解决,那么请转换成字符串吧!让go和js用各自的方法分别处理一波,得到自己想要的结果或者给出各自想给的数据。</p> +<h2 id="js调用go函数">js调用go函数</h2> +<blockquote> +<p>此处需要在go中引入<a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a>,以实现js相关的操作。</p> +</blockquote> +<h3 id="注册go函数">注册go函数</h3> +<p>将go的函数注册为js的函数,由js来进行调用。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleCount</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">count</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Int</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">js</span><span class="p">.</span><span class="nf">ValueOf</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleEvent&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleEvent</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>js.Func()</code> 接受一个函数类型作为其参数,该函数的定义是固定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"><span class="c1">// this 即 JavaScript 中的 this +</span></span></span><span class="line"><span class="cl"><span class="c1">// args 是在 JavaScript 中调用该函数的参数列表。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 返回值需用 js.ValueOf 映射成 JavaScript 的值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>js.ValueOf返回作为js的值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用">js调用</h3> +<p>在js中使用也非常简单,引入wasm文件之后,直接调用函数即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleEvent</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">// 传入参数1 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go调用js函数">go调用js函数</h2> +<p>如果在js中本身已经定义了函数,那么在go中也可以直接调用该函数,进行运算,将得出的结果在go中继续使用。</p> +<h3 id="定义js函数">定义js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="go中调用js函数">go中调用js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;add&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="c1">// 此处输出类型为js.Value,无法直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nf">Int</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// 使用.Int()将其转换为go中的类型,即可直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入wasm">引入wasm</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="结果">结果</h3> +<p>在前端调试台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;number: 3&gt; +</span></span><span class="line"><span class="cl">4 +</span></span></code></pre></td></tr></table> +</div> +</div><p>第一个结果就是js.Value的值,第二个结果则是转换成go的值,并按照逻辑进行了<code>+1</code>处理。</p> +<h2 id="回调函数解决go函数阻塞问题">回调函数/解决go函数阻塞问题</h2> +<blockquote> +<p>The Go function fn is called with the value of JavaScript&rsquo;s &ldquo;this&rdquo; keyword and the arguments of the invocation. The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.</p> +<p>Invoking the wrapped Go function from JavaScript will pause the event loop and spawn a new goroutine. Other wrapped functions which are triggered during a call from Go to JavaScript get executed on the same goroutine.</p> +<p>As a consequence, if one wrapped function blocks, JavaScript&rsquo;s event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.</p> +</blockquote> +<p><code>syscall/js</code>官方文档表明,如果go包装函数阻塞,那么js的事件循环也将被阻塞,直到函数返回,调用任何需要事件循环(如fetch)的异步js api都导致立即死锁。因此,一个阻塞函数应该显式地启动一个新的协程。</p> +<p>此处,可以在go中注册一个回调函数,加上协程实现异步,不会产生堵塞。</p> +<h3 id="注册函数">注册函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">username</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">String</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;hello, %s !&#34;</span><span class="p">,</span> <span class="nx">username</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-1">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleRender</span><span class="p">(</span><span class="s2">&#34;tom&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">hello, tom ! // 隔了3秒之后,输出了回调函数的值 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go实现promise">Go实现Promise</h2> +<p>上一步的回调函数,解决了函数阻塞问题,此处,结合回调函数实现promise,来丰富异步场景。</p> +<p>在js中,promise是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>async</code>和<code>await</code>调用,拿到结果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">printMessage</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">message</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在go中又如何构建promise呢?这里可以用到上述go与js的类型转换,创建一个promise:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="注册函数-1">注册函数</h3> +<p>go的完整实现如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">handler</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="s">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">promiseConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">promiseConstructor</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-2">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">HandleRender</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出-1">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">message: hello, world ! // 隔了3秒之后输出 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="操作dom">操作DOM</h2> +<h3 id="使用document">使用document</h3> +<p>定义一个全局的document</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">var docuemnt = js.Global().Get(&#34;document&#34;) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="获取元素">获取元素</h3> +<p>获取一个<code>id</code>为<code>container</code>的<code>div</code>,设置<code>background-color: red</code>、<code>widht: 600</code>、<code>height: 400</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;container&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElementStyle</span> <span class="p">=</span> <span class="nx">container</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;style&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;background&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="s">&#34;600px&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="s">&#34;400px&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建元素">创建元素</h3> +<p>创建一个<code>id</code>为<code>image</code>的<code>image</code>,设置<code>width:300</code>、<code>height:200</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">imageElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;createElement&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加子元素">添加子元素</h3> +<p>将<code>image</code>添加为<code>id</code>为<code>container</code>的<code>div</code>的子元素</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">containerElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;appendChild&#34;</span><span class="p">,</span> <span class="nx">imageElement</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加事件">添加事件</h3> +<p>给<code>image</code>添加右击事件,右击<code>image</code>则阻止右键菜单</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 定义响应函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">handlePreventEventCallBack</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;preventDefault&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 给image添加事件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;contextmenu&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handlePreventEventCallBack</span><span class="p">))</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里需要注意的是,当不再调用响应事件函数时,必须调用Func.Release以释放资源:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">cb</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Func</span> +</span></span><span class="line"><span class="cl"><span class="nx">cb</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="nx">any</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;button clicked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">cb</span><span class="p">.</span><span class="nf">Release</span><span class="p">()</span> <span class="c1">// 如果不再单击该按钮,则释放该函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;myButton&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;click&#34;</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="canvas">Canvas</h2> +<p>这里放一波canvas的案例,包含了一些常用方法,可以参考完成更多操作。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;math&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">radius</span> <span class="p">=</span> <span class="mi">200</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">canvas</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="nx">height</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getContext&#34;</span><span class="p">,</span> <span class="s">&#34;2d&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;beginPath&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;arc&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="nx">math</span><span class="p">.</span><span class="nx">Pi</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;lightpink&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fill&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;lineWidth&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;strokeStyle&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;stroke&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;font&#34;</span><span class="p">,</span> <span class="s">&#34;20px Comic Sans MS&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;blue&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fillText&#34;</span><span class="p">,</span> <span class="s">&#34;Hello, World !&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="o">-</span><span class="mi">60</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>渲染结果:</p> +<p><img src="https://user-images.githubusercontent.com/25321169/217725808-5dd94da2-bb43-4129-95a9-27f3f85daba5.png" + + + + loading="lazy" + + alt="go_wasm_canvas" + + +></p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a></li> +<li><a class="link" href="https://github.com/golang/go/wiki/WebAssembly" target="_blank" rel="noopener" + >go wiki WebAssembly</a></li> +<li><a class="link" href="https://geektutu.com/post/quick-go-wasm.html" target="_blank" rel="noopener" + >Go WebAssembly (Wasm) 简明教程</a></li> +<li><a class="link" href="https://withblue.ink/2020/10/03/go-webassembly-http-requests-and-promises.html" target="_blank" rel="noopener" + >Go, WebAssembly, HTTP requests and Promises</a></li> +</ul> + + + + wow.js和animate css在vue3中的应用 + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + Sat, 28 Jan 2023 16:09:44 +0000 + + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + <h2 id="环境">环境</h2> +<ul> +<li>vue 3.2</li> +<li>typescript 4.7.4</li> +<li>wow.js 1.2.2</li> +<li>animate.css 4.1.1</li> +</ul> +<h2 id="animatecss">animate.css</h2> +<h3 id="下载">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add animate.css -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;animate.css&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<p>需要注意的是,animate css在4.0之后使用<code>animate__</code>前缀</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="动画延迟">动画延迟</h3> +<h4 id="官方方法">官方方法</h4> +<p>官方给出的动画延迟是<code>animate__delay-2s</code>、<code>animate__delay-3s</code> &hellip;&hellip;</p> +<p>直接在class中添加即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animate__delay-2s&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="自定义延迟">自定义延迟</h4> +<p>特殊场景需要使用不同于官方的延迟时间,因此可以自定义延迟时间,直接声明延迟的类,然后在class上加入即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-1</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">100</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">300</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-3</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">500</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-4</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">700</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-5</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">900</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-2&#34;</span><span class="p">&gt;</span>Another animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="wowjs">wow.js</h2> +<h3 id="下载-1">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add wow.js -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">WOW</span> <span class="nx">from</span> <span class="s1">&#39;wow.js&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">WOW</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">boxClass</span><span class="o">:</span> <span class="s1">&#39;wow&#39;</span><span class="p">,</span> <span class="c1">// 类名,在用户滚动时显示隐藏的框。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">animateClass</span><span class="o">:</span> <span class="s1">&#39;animate__animated&#39;</span><span class="p">,</span> <span class="c1">// 触发CSS动画的类名称 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">offset</span><span class="o">:</span> <span class="mi">300</span><span class="p">,</span> <span class="c1">// 定义浏览器视口底部与隐藏框顶部之间的距离。当用户滚动并到达此距离时,隐藏的框会显示出来。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mobile</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在移动设备上打开/关闭WOW.js。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">live</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在页面上同时检查新的WOW元素。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}).</span><span class="nx">init</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用-1">使用</h3> +<p>使用<code>wow</code>直接替代<code>animate__animated</code>即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;wow animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>由于写一个页面需要使用到wow,好多年没用过了,查了一下文档超多版本教程,使用起来各种不成功,难受&hellip;暂时也没找到可替代的方案&hellip;</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/graingert/wow" target="_blank" rel="noopener" + >wow</a></li> +<li><a class="link" href="https://animate.style/" target="_blank" rel="noopener" + >animate css</a></li> +</ul> + + + + python3 socket tcp example + https://blog.hunterji.com/p/python3-socket-tcp-example/ + Thu, 09 Jun 2022 22:57:54 +0000 + + https://blog.hunterji.com/p/python3-socket-tcp-example/ + <h2 id="socket-tcp-server">socket tcp server</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_tcp_server</span><span class="p">(</span><span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s1">&#39;0.0.0.0&#39;</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">9000</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket tcp 服务端 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务器tcp server接收信息的端口, 默认9000 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param buffer_size: 套接字缓冲区大小, 默认1024 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;服务端开始运行...</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">client</span><span class="p">,</span> <span class="n">sender_info</span> <span class="o">=</span> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">receive_data</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;客户端地址: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sender_info</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;来自客户端的信息: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">receive_data</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1"># 返回消息</span> +</span></span><span class="line"><span class="cl"> <span class="n">client</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;response...&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_tcp_server</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="socket-tcp-client">socket tcp client</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_tcp_client_send_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket tcp 客户端发送消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param message: 消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务端的ip地址 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务端的端口号 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">family</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">response</span> <span class="o">=</span> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;response : </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">decode</span><span class="p">()))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_tcp_client_send_message</span><span class="p">(</span><span class="s1">&#39;hello,world!&#39;</span><span class="p">,</span> <span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">9000</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + python3 socket udp example + https://blog.hunterji.com/p/python3-socket-udp-example/ + Thu, 09 Jun 2022 22:57:54 +0000 + + https://blog.hunterji.com/p/python3-socket-udp-example/ + <h2 id="socket-udp-server">socket udp server</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_udp_server</span><span class="p">(</span><span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s1">&#39;0.0.0.0&#39;</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">9000</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket udp 服务端 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务器udp server接收信息的端口, 默认9000 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param buffer_size: 套接字缓冲区大小, 默认1024 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_DGRAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;服务端开始运行...</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">receive_data</span><span class="p">,</span> <span class="n">sender_info</span> <span class="o">=</span> <span class="n">udp_socket</span><span class="o">.</span><span class="n">recvfrom</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;客户端地址: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sender_info</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;来自客户端的信息: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">receive_data</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_udp_server</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="socket-udp-client">socket udp client</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_udp_client_send_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket udp 客户端发送消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param message: 消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务端的ip地址 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务端的端口号 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_client_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">family</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">SOCK_DGRAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_client_socket</span><span class="o">.</span><span class="n">sendto</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="p">(</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_udp_client_send_message</span><span class="p">(</span><span class="s1">&#39;hello,world!&#39;</span><span class="p">,</span> <span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">9000</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + VUE3+TS+微前端实践 + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + Wed, 16 Feb 2022 15:07:56 +0000 + + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + <h2 id="前言">前言</h2> +<p>基于架构的调整,前端开始转为微前端。经过调研,决定使用<a class="link" href="https://qiankun.umijs.org/zh/guide/getting-started" target="_blank" rel="noopener" + >qiankun</a>微服务框架来使用,本文将介绍VUE3+TS+qiankun的实践经过。微服务架构的优势之一在于可以结合不同技术栈的节点,基于技术栈的考虑,此处用的都是vue3。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>vue 3.0.0</li> +<li>TypeScript 4.1.5</li> +<li>vue router 4.0.0</li> +<li>@vue/cli 4.5.15</li> +<li>qiankun 2.6.3</li> +</ul> +<h2 id="实践">实践</h2> +<h3 id="架构">架构</h3> +<p><img src="https://blog.hunterji.com/../assets/qiankun_example.jpg" + + + + loading="lazy" + + alt="qiankun_example" + + +></p> +<p>如上图所示,微服务架构将会由多个节点构成,首先由一个主节点<code>site_base</code>连接所有子节点,子节点可以不断拓展。</p> +<h3 id="主节点">主节点</h3> +<p>主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base</a></p> +<p>创建主节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_base +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_base +</span></span></code></pre></td></tr></table> +</div> +</div><p>安装<code>qiankun</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add qiankun +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/App.vue</code>中添加路由和渲染节点</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;nav&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/about&#34;</span><span class="p">&gt;</span>About<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site1&#34;</span><span class="p">&gt;</span>Site1<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site2&#34;</span><span class="p">&gt;</span>Site2<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span><span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site1&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site2&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/main.ts</code>中引入子节点配置</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id,对应上一步中src/App.vue中的渲染节点 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9002/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site2&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> <span class="c1">// 注册应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="子节点">子节点</h3> +<p>子节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1</a></p> +<p>此处以<code>site1</code>为例,<code>site2</code>同理。</p> +<p>创建子节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_1 +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_1 +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/App.vue</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/views/Home.vue</code>,修改其内容,写一点标识性的文本</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Hello, Site1!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/pulic-path.ts</code>,第一行的注视一定要加,避免eslint对于变量的报错</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="cm">/* eslint-disable camelcase */</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">((</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">__webpack_public_path__</span> <span class="o">=</span> <span class="p">(</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__INJECTED_PUBLIC_PATH_BY_QIANKUN__</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/router/index.ts</code>,此处直接返回<code>routes</code>,而不是<code>router</code>,并且修改</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">RouteRecordRaw</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">RouteRecordRaw</span><span class="p">&gt;</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Home&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/Home.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/about&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;About&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/About.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 直接返回routes,由其它地方处理创建路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">routes</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;./public-path&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createRouter</span><span class="p">,</span> <span class="nx">createWebHistory</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">routes</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">instance</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">history</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">render</span> <span class="p">(</span><span class="nx">props</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="p">{</span> <span class="nx">container</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">props</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当为微服务主节点情况下访问,会设置二级路径,而直接访问时没有二级路径,此处需要根据实际情况修改 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">history</span> <span class="o">=</span> <span class="nx">createWebHistory</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span> <span class="o">?</span> <span class="s1">&#39;/site1&#39;</span> <span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="nx">createRouter</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">routes</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">mount</span><span class="p">(</span><span class="nx">container</span> <span class="o">?</span> <span class="nx">container</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> <span class="o">:</span> <span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">bootstrap</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;%c &#39;</span><span class="p">,</span> <span class="s1">&#39;color: green &#39;</span><span class="p">,</span> <span class="s1">&#39;vue3.0 app bootstraped&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">storeTest</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="k">void</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`[onGlobalStateChange - </span><span class="si">${</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">]:`</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ignore</span>: <span class="kt">props.name</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span>: <span class="kt">props.name</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">mount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">storeTest</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$onGlobalStateChange</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$setGlobalState</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">unmount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">unmount</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">_container</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">.</span><span class="nx">destroy</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>vue.config.js</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">name</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./package&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">resolve</span> <span class="p">(</span><span class="nx">dir</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="nx">dir</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">port</span> <span class="o">=</span> <span class="mi">9001</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputDir</span><span class="o">:</span> <span class="s1">&#39;dist&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assetsDir</span><span class="o">:</span> <span class="s1">&#39;static&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filenameHashing</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">devServer</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">hot</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">disableHostCheck</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">overlay</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">warnings</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">errors</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Access-Control-Allow-Origin&#39;</span><span class="o">:</span> <span class="s1">&#39;*&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 自定义webpack配置 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">configureWebpack</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alias</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;@&#39;</span><span class="o">:</span> <span class="nx">resolve</span><span class="p">(</span><span class="s1">&#39;src&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">output</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 把子应用打包成 umd 库格式 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">library</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">-[name]`</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">libraryTarget</span><span class="o">:</span> <span class="s1">&#39;umd&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonpFunction</span><span class="o">:</span> <span class="sb">`webpackJsonp_</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证">验证</h3> +<p>主节点和子节点分别独立运行,但是子节点的地址需要跟主节点配置中子节点对应的地址相同。</p> +<p>在主节点上点击子节点的路由,即可在主节点上访问子节点的页面了!</p> +<img src="../assets/qiangkun_example_result.png" alt="qiangkun_example_result" style="zoom:50%;" /> +<h2 id="主节点优化">主节点优化</h2> +<p>主节点除了如上配置,可以进行两项优化:</p> +<ul> +<li>模块化子节点配置</li> +<li>添加过渡状态,当加载子节点时窗口顶部出现加载进度条</li> +</ul> +<p>优化后主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize</a></p> +<h3 id="模块化子节点配置">模块化子节点配置</h3> +<p>创建文件夹<code>src/childNodes</code>,然后创建文件<code>src/childNodes/apps.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">apps</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">start</span> <span class="kr">from</span> <span class="s1">&#39;./childNodes&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="过渡效果">过渡效果</h3> +<p>此处的过渡效果采用<code>NProgress</code>库,先来安装一波</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add nprogress +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addGlobalUncaughtErrorHandler</span><span class="p">,</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">NProgress</span> <span class="kr">from</span> <span class="s1">&#39;nprogress&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;nprogress/nprogress.css&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点加载前 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">beforeLoad</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开始进度条 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点挂载后 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">afterMount</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">done</span><span class="p">()</span> <span class="c1">// 进度条结束 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p><code>qiankun</code>框架确实挺不错的,配置也并不是复杂,但是唯一想吐槽的一点是对于ts的支持感觉不太好/狗头,或许是我写得不够好吧,后面会持续优化使用。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://qiankun.umijs.org/zh/guide" target="_blank" rel="noopener" + >qiankun官方文档</a></li> +<li><a class="link" href="https://juejin.cn/post/6981656757458173988" target="_blank" rel="noopener" + >vue3+ts+qiankun的微前端快速上手</a></li> +</ul> + + + + VUE项目国际化 + https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/ + Wed, 29 Dec 2021 14:24:30 +0000 + + https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/ + <h2 id="前言">前言</h2> +<p><code>i18n</code>是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。 在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。</p> +<p>Node.js本身有一个<code>i18n</code>的包,但是为了更好地结合vue,此处我们使用的是<code>vue-i18n</code>。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Vue 3.0.0</li> +<li>TypeScript 4.5.4</li> +<li>Vue Cli 4.5.15</li> +</ul> +<h2 id="安装">安装</h2> +<p>使用vue cli安装,会自动生成文件且引入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue add i18n +</span></span></code></pre></td></tr></table> +</div> +</div><p>执行过程中将需要填写如下问题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">locale</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="w"> </span><span class="n">localization</span><span class="p">.</span><span class="w"> </span><span class="p">[</span><span class="err">默认选项</span><span class="p">]</span><span class="n">en</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">fallback</span><span class="w"> </span><span class="n">locale</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="w"> </span><span class="n">localization</span><span class="p">.</span><span class="w"> </span><span class="p">[</span><span class="err">默认选项</span><span class="p">]</span><span class="n">en</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">directory</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">store</span><span class="w"> </span><span class="n">localization</span><span class="w"> </span><span class="n">messages</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="p">.</span><span class="w"> </span><span class="n">It</span><span class="s1">&#39;s stored under `src` directory. [默认选项]locales +</span></span></span><span class="line"><span class="cl"><span class="s1">? Enable legacy API (compatible vue-i18n@v8.x) mode ? [默认选项]No +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>执行完成之后,将会自动处理如下文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// vue add i18n执行结束后的git status +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">new file: .env // 执行过程中的第一个和第二个问题将使用环境变量更新 +</span></span><span class="line"><span class="cl">modified: package.json +</span></span><span class="line"><span class="cl">new file: src/components/HelloI18n.vue // 官方给出的demo组件 +</span></span><span class="line"><span class="cl">new file: src/i18n.ts // 自动读取多语言的json文件且创建i18n实例 +</span></span><span class="line"><span class="cl">new file: src/locales/en.json // 多语言json文件所在文件夹,默认选择的en语言,所以生成一个默认的语言json文件 +</span></span><span class="line"><span class="cl">modified: src/main.ts // 引入i18n +</span></span><span class="line"><span class="cl">new file: vue.config.js // 配置i18n +</span></span><span class="line"><span class="cl">modified: yarn.lock +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多语言配置">多语言配置</h2> +<p>想要多语言则需要配置多个对应语言的json文件,其字段必须相同,否则当用户切换时,会出现找不到该字段对应文字的问题。</p> +<p>为了便于切换和处理,此处不再使用<code>en</code>之类的简写,而是和系统语言名称对应起来,此处将使用<code>zh_CN</code>和<code>en_GB</code>来做配置。</p> +<h3 id="创建多语言json文件">创建多语言json文件</h3> +<p>此处生成两个文件<code>src/locales/zh_CN.json</code>和<code>src/locales/en_GB.json</code>,内容分别如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;中文&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;你好,世界!&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;English&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello, World !&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="更新语言标识">更新语言标识</h3> +<p>将相关文件的<code>en</code>都改成<code>en_GB</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">##env +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">VUE_APP_I18N_LOCALE=en_GB +</span></span><span class="line"><span class="cl">VUE_APP_I18N_FALLBACK_LOCALE=en_GB +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/i18n.ts +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">createI18n</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">legacy</span>: <span class="kt">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span>: <span class="kt">process.env.VUE_APP_I18N_LOCALE</span> <span class="o">||</span> <span class="s1">&#39;en_GB&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fallbackLocale</span>: <span class="kt">process.env.VUE_APP_I18N_FALLBACK_LOCALE</span> <span class="o">||</span> <span class="s1">&#39;en_GB&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">messages</span>: <span class="kt">loadLocaleMessages</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<h3 id="重写首页">重写首页</h3> +<p>此处重写<code>src/views/Home.vue</code>文件,先按照正常的内容去写,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nx">Hello</span><span class="p">,</span> <span class="nx">World</span> <span class="o">!</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置多语言">配置多语言</h3> +<p>需要配置多语言的地方就是展示的文字,所以将该文字替换掉即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useI18n</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue-i18n&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">t</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useI18n</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处引入<code>t</code>方法,而其参数<code>message</code>为上面配置的每一个json文件中的<code>message</code>字段,其将用json对应字段的value来展示。</p> +<p>可能有小伙伴儿要问,那如果不是html中的展示文字怎么办呢?</p> +<p>这也一样使用<code>t</code>方法的,示例如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="na">:placeholder</span><span class="o">=</span><span class="s">&#34;t(&#39;message&#39;)&#34;</span> <span class="p">/&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useI18n</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue-i18n&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">t</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useI18n</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">exampleVal</span> <span class="o">=</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处我们的json结构都是单层的,如果是嵌套的结构的话,可以使用<code>t('home.message')</code>这样的方式来引用。</p> +<h3 id="切换语言">切换语言</h3> +<p>此处需要引入<code>locale</code>,主要通过更新locale.value来切换语言。此处的切换是全局的,所以只需要写一个切换组件,其它组件都将会被切换语言。</p> +<p>写一个下拉框来实现语言的切换,完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">select</span> <span class="nt">v-model</span><span class="s">=&#34;state.language&#34; @change=&#34;handleLanguageChange&#34;&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;zh_CN&#34;&gt;zh_CN&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;en_GB&#34;&gt;en_GB&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/select&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/div&gt; +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;/template&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;script setup lang=&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">reactive</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">useI18n</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="nt">-i18n</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="p">{</span> <span class="na">t</span><span class="err">,</span> <span class="na">locale</span> <span class="p">}</span> <span class="err">=</span> <span class="na">useI18n</span><span class="err">()</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">state</span><span class="o">:</span> <span class="p">{</span> <span class="na">language</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="err">=</span> <span class="na">reactive</span><span class="err">(</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="na">language</span><span class="o">:</span> <span class="err">&#39;</span><span class="na">en_GB</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">)</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">handleLanguageChange </span><span class="o">=</span> <span class="err">(</span><span class="na">option</span><span class="o">:</span> <span class="p">{</span> <span class="na">target</span><span class="o">:</span> <span class="p">{</span> <span class="na">value</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="p">}</span><span class="err">)</span> <span class="err">=</span><span class="p">&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 主要通过更新locale.value来切换语言 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">option</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>通过下拉框切换选项可以切换语言。</p> +<h3 id="本地环境">本地环境</h3> +<p>虽然<code>i18n</code>设置了默认的语言,但是友好的交互应当是根据用户的环境语言加载。这里需要使用<code>navigator.language</code>来拿到用户的环境语言,通过判断环境语言切换初始的语言配置。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">language</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;zh-CN&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;en-GB&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>完整代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">select</span> <span class="nt">v-model</span><span class="s">=&#34;state.language&#34; @change=&#34;handleLanguageChange&#34;&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;zh_CN&#34;&gt;zh_CN&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;en_GB&#34;&gt;en_GB&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/select&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/div&gt; +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;/template&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;script setup lang=&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">reactive</span><span class="err">,</span> <span class="na">onMounted</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">useI18n</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="nt">-i18n</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="p">{</span> <span class="na">t</span><span class="err">,</span> <span class="na">locale</span> <span class="p">}</span> <span class="err">=</span> <span class="na">useI18n</span><span class="err">()</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">state</span><span class="o">:</span> <span class="p">{</span> <span class="na">language</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="err">=</span> <span class="na">reactive</span><span class="err">(</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="na">language</span><span class="o">:</span> <span class="err">&#39;</span><span class="na">en_GB</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">)</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">handleLanguageChange </span><span class="o">=</span> <span class="err">(</span><span class="na">option</span><span class="o">:</span> <span class="p">{</span> <span class="na">target</span><span class="o">:</span> <span class="p">{</span> <span class="na">value</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="p">}</span><span class="err">)</span> <span class="err">=</span><span class="p">&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 主要通过更新locale.value来切换语言 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">option</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fetchData</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">language</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 环境语言中间的线是居中的 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">case</span> <span class="s1">&#39;zh-CN&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;en-GB&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">onMounted</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fetchData</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>运行之后,默认语言不再是英语,而是跟环境语言一致的。</p> +<h3 id="参考文档">参考文档</h3> +<ul> +<li><a class="link" href="https://kazupon.github.io/vue-i18n/zh/started.html" target="_blank" rel="noopener" + >vue-i18n</a></li> +</ul> + + + + electron的__dirname not defined报错解决 + https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:29:14 +0000 + + https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue cli plugin electron builder开发项目,用到涉及node相关的功能,比如<code>fs</code>、<code>path</code>,出现报错<code>__dirname not defined </code>。</p> +<h2 id="解决">解决</h2> +<p>该问题在于需要开启electron对于node操作的支持。</p> +<h3 id="安装typesnode">安装@types/node</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add @types/node -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="修改配置">修改配置</h3> +<p>修改electron的配置文件,此处我的配置文件为<code>src/background.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegrationInWorker</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue3+ts+electron不支持require is not defined报错解决 + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:23:46 +0000 + + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue3+typescript+electron开发时,遇到一个报错为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Uncaught ReferenceError: require is not defined +</span></span></code></pre></td></tr></table> +</div> +</div><p>点进去是<code>module.exports = require(&quot;events&quot;)</code>,并不是自己的代码中的<code>require</code>,因此无法改变写法只能让项目去支持它。</p> +<h2 id="解决">解决</h2> +<p>在electron的配置文件中,新增或者修改如下配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">contextIsolation</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/64706829/electron-tedious-requireevents-is-not-defined" target="_blank" rel="noopener" + >Electron/Tedious: require(&ldquo;events&rdquo;) is not defined</a></li> +</ul> + + + + uniapp canvas生成海报功能拆解和问题记录 + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + Thu, 25 Nov 2021 17:07:55 +0000 + + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + <h2 id="前言">前言</h2> +<p>最近在用uniapp开发小程序,需要用到canvas画海报然后再保存本地。</p> +<p>之前写过同样功能的<a class="link" href="https://github.com/Kuari/Blog/issues/1" target="_blank" rel="noopener" + >文章</a>,不过场景不同,之前是在web上生成海报,该场景可以使用之前文章的方法——html转canvas来实现。</p> +<p>但是uniapp则不同,该框架是去DOM化的,因此只能使用uniapp的官方canvas来实现。</p> +<h2 id="功能拆解">功能拆解</h2> +<p>网上找到的文章,有几篇写得挺好的,展现了完整的功能。这里我把用到的几个功能拆解出来,而不用先通读整篇代码。</p> +<h3 id="创建canvas">创建canvas</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">canvas</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width: 300px; height: 200px;&#34;</span> <span class="na">canvas-id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">canvas</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onReady</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="背景色">背景色</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;red&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 此处其实绘制了一个300x200的红色矩形,但是其大小跟canvas大小相同即为背景色了 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="加载图片">加载图片</h3> +<h4 id="1本地图片">1)本地图片</h4> +<p>图片直接在项目中,可以直接加载。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="s2">&#34;./background.png&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="2url">2)url</h4> +<p>此处url返回的为文件流,在uniapp中无法直接加载,需要转换成本地信息才可以使用。</p> +<p>有两种方式可以使用,小程序都需要添加download合法域名:</p> +<ul> +<li><em><strong>uni.getImageInfo</strong></em>:获取文件信息,我使用的这个方法</li> +<li><em><strong>uni.downloadFile</strong></em>:下载文件</li> +</ul> +<p>原生使用方法是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>由于js的异步问题,如果图片较大或者多个图片的情况下,会有这边还没加载完,canvas就已经绘制完了的情况,所以这里将其优化下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 首先封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">url</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3base64">3)base64</h4> +<p>如果你的图片数据是base64的,那恭喜你,依然加载不了。当然这存在的情况是,微信开发工具是没有问题的,但是上了真机之后直接无法加载了,这波是小程序的锅。</p> +<p>这里呢需要将图片存储然后用本地地址绘制。</p> +<p>原生方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>优化一波:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getBase64ImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">base64Data</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="nx">base64Data</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getBase64ImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="文字">文字</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFontSize</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">font</span> <span class="o">=</span> <span class="s2">&#34;nomarl bold 13px Arial,sans-serif&#34;</span> <span class="c1">// 加粗等功能 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillText</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="圆角矩形">圆角矩形</h3> +<p>想要绘制一个圆角的矩形,啊&hellip;&hellip;这波就复杂了,原理就不细讲了,直接上代码,调用即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">roundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">width</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">height</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">drawRoundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">strokeStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">fillStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">roundedRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">strokeStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">fillStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">drawRoundedRect</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">86</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="图片加载为圆形">图片加载为圆形</h3> +<p>基本原理是,正常加载图片,canvas画个圆给它裁剪掉,上代码!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 如果小伙伴儿调试时候感觉圆形和图片有点错位,可以开启下面两行注释代码,给圆圈加个边框 +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.setStrokeStyle(&#39;#AAAAAA&#39;) +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.stroke() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">clip</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">image</span><span class="o">-</span><span class="nx">path</span><span class="o">&gt;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">restore</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas生成的海报下载">canvas生成的海报下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">savePoster</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;确定保存到相册吗&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">canvasToTempFilePath</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvasId</span><span class="o">:</span> <span class="s1">&#39;sharePoster&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">saveImageToPhotosAlbum</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">tempFilePath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 此处为执行成功 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">openSetting</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">authSetting</span><span class="p">[</span><span class="s1">&#39;scope.writePhotosAlbum&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限成功,再次点击图片即可保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限失败,无法保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="k">this</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="问题">问题</h2> +<h3 id="图片有时显示有时不显示">图片有时显示有时不显示</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.加载图片”优化代码处,将加载图片全部写成同步的,在开始绘制前将图片全都加载好。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="base64数据的图片在小程序开发工具显示到了真机就不显示了">base64数据的图片在小程序开发工具显示,到了真机就不显示了</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.3)base64”优化代码处,使用该方法即可。小程序canvas无法直接加载base64图片。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas整体画成圆角的">canvas整体画成圆角的</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">canvas背景是透明色,只要画个大小覆盖canvas的圆角矩形或者使用圆角背景图即可。 +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue禁止遮罩层下的页面滚动 + https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/ + Thu, 25 Nov 2021 16:13:10 +0000 + + https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/ + <h2 id="问题">问题</h2> +<p>功能开发过程中写遮罩时,遇到遮罩下页面还可以滚动的问题。</p> +<h2 id="解决">解决</h2> +<p>直接给遮罩下的元素套上一个样式,使其不可滚动。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">:class</span><span class="o">=</span><span class="s">&#34;isPopup ? &#39;disableRoll&#39; : &#39;&#39;&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> ... +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">isPopup</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nc">disableRoll</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">overflow</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue3 script setup响应式初体验 + https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/ + Thu, 04 Nov 2021 20:48:09 +0000 + + https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/ + <h2 id="前言">前言</h2> +<p>最近空下来,正好找个项目尝鲜,把vue3+ts+setup哐哐全堆上,试试最新的前端技术。</p> +<p>从最先体会到的变化,就是关于响应式APIs了。遇到不好问题,怪我没有理解文档/狗头。比如说:</p> +<ul> +<li>明明这个数据改了,怎么没渲染出来?</li> +<li>同样是Arrary,怎么套了个reactive就类型不一样了?</li> +</ul> +<p>所以这里基于遇到的几个问题,来写个笔记。</p> +<h2 id="简单对比">简单对比</h2> +<ul> +<li><a class="link" href="https://v3.cn.vuejs.org/api/sfc-script-setup.html#%E5%93%8D%E5%BA%94%E5%BC%8F" target="_blank" rel="noopener" + >响应式官方文档</a></li> +</ul> +<p>官方案例如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ref</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="nt">@click</span><span class="s">=&#34;count++&#34;</span><span class="p">&gt;{{</span> <span class="na">count</span> <span class="p">}}&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们可以先看看,如果是vue2,怎么做呢?</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">count</span><span class="o">:</span> <span class="mi">0</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="nt">@click</span><span class="s">=&#34;count++&#34;</span><span class="p">&gt;{{</span> <span class="na">count</span> <span class="p">}}&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以很明显地看到此处的<code>count</code>跟上面的官方文档不同,使用了<code>ref</code>方法。这就是setup中的响应式APIs,需要预先声明响应式变量。</p> +<p>如果,不声明呢?那就是直接写成如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>运行一下,首先你会发现,没有任何报错,代码正常运行。但是当你在浏览器上查看,开始改变<code>count</code>的值时,就会发现,怎么页面没有变化?</p> +<p>所以,需要手动命名响应,才会在值变化时触发视图渲染。</p> +<h2 id="ref">ref</h2> +<p>现在来详细讲讲<code>ref</code>,该方法常用于单个变量,比如:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ref</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;hello&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里需要说明一个问题,那就是<code>ref(&quot;hello&quot;) !== &quot;hello&quot;</code>。这就是我说的,为什么同样的值,类型就不同了。那是因为ref返回的是一个<code>Proxy</code>,而非原来的值。在视图中可直接使用,但是在js/ts中操作,需要使用<code>.value</code>来操作,如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;kuari&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">changeName</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;tom&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="reactive">reactive</h2> +<p><code>reactive</code>不同于<code>ref</code>的点在于,其是“深层”的——它影响所有嵌套 property。也就是说,其可用在对象或者数组上。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">form</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">desc</span><span class="o">:</span> <span class="s2">&#34;developer&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>其返回类型也是<code>Proxy</code>,不同点在于,可以直接修改某一个元素的,如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">form</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="s2">&#34;tom&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>但是如果你想整个替换就会报错了。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">form</span> <span class="o">=</span> <span class="p">{...}</span> <span class="c1">// 报错,类型不同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>当使用的是数组时,如果想整个替换,可以将其写成对象,代码如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">selected</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">arr</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;vue&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span>: <span class="kt">0</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;typescript&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span>: <span class="kt">1</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">selected</span><span class="p">.</span><span class="nx">arr</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 整个替换 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">selected</span><span class="p">.</span><span class="nx">arr</span> <span class="o">=</span> <span class="p">[...]</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>关于<code>reactive</code>跟<code>ref</code>一起使用,<code>reactive</code> 将解包所有深层的<code>ref</code>,同时维持 ref 的响应性。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> <span class="nx">count</span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// ref 会被解包 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span> <span class="o">===</span> <span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 它会更新 `obj.count` +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="o">++</span> +</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// 2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="p">)</span> <span class="c1">// 2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 它也会更新 `count` ref +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="o">++</span> +</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="p">)</span> <span class="c1">// 3 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// 3 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="总结">总结</h2> +<p><code>setup</code>总体来说,用起来真的会更加简洁,而响应式虽然好像比之前麻烦些了,但是一定层面上让开发对对于程序有了更深入的操控。墙裂推荐一波!后面再来详细讲讲对于新特性的体验。</p> + + + + 你是个成熟的代码要学会自己按需引入了 + https://blog.hunterji.com/p/%E4%BD%A0%E6%98%AF%E4%B8%AA%E6%88%90%E7%86%9F%E7%9A%84%E4%BB%A3%E7%A0%81%E8%A6%81%E5%AD%A6%E4%BC%9A%E8%87%AA%E5%B7%B1%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5%E4%BA%86/ + Wed, 27 Oct 2021 15:28:00 +0000 + + https://blog.hunterji.com/p/%E4%BD%A0%E6%98%AF%E4%B8%AA%E6%88%90%E7%86%9F%E7%9A%84%E4%BB%A3%E7%A0%81%E8%A6%81%E5%AD%A6%E4%BC%9A%E8%87%AA%E5%B7%B1%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5%E4%BA%86/ + <h2 id="前言">前言</h2> +<p>前端小伙伴儿们是不是经常遇到ui组件全局引入导致体积太大,按需引入导致不断手写会很麻烦。所以,当当!今天我们来让代码自己按需引入,解放前端小伙伴儿们的生产力,早日实现下班自由!(甲方:我要再改十个需求!)</p> +<h2 id="介绍">介绍</h2> +<p>我们这里介绍的是<code>unplugin-vue-components</code>。该组件是由vue核心开发成员antfu开发的,尤大也是推荐的,且是该项目的金牌赞助商。</p> +<p>该组件主要是为了实现vue项目的组件自动引入。</p> +<p>官方文档:<a class="link" href="https://github.com/antfu/unplugin-vue-components" target="_blank" rel="noopener" + >antfu/unplugin-vue-components</a></p> +<h2 id="完整案例">完整案例</h2> +<p>此处我们以vite为例,来主要看一下其对于自定义组件和UI库组件的自动按需引入。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/unplugin_auto_import" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/unplugin_auto_import</a></p> +<h3 id="创建项目">创建项目</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite unplugin_auto_import --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后进入文件夹安装依赖。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> unplugin_auto_import +</span></span><span class="line"><span class="cl">yarn install +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="安装unplugin-vue-components">安装unplugin-vue-components</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D unplugin-vue-components +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置viteconfigjs">配置vite.config.js</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Components</span> <span class="nx">from</span> <span class="s1">&#39;unplugin-vue-components/vite&#39;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="nx">vue</span><span class="p">(),</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Components</span><span class="p">({</span> <span class="cm">/* options */</span> <span class="p">})</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自动引入自定义组件">自动引入自定义组件</h3> +<p>我们默认模板创建的项目中,默认在<code>App.vue</code>中引入了<code>./components/HelloWorld.vue</code>。此处就可以来尝试下如何自动引入了。</p> +<p>在配置了<code>unplugin-vue-components</code>之后,现在只需要删除引入行(其实这时候打开vscode就会发现改行已经灰掉了),被删除行如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">HelloWorld</span> <span class="nx">from</span> <span class="s1">&#39;./components/HelloWorld.vue&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>删除以后完整<code>App.vue</code>如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Vue logo&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./assets/logo.png&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hello Vue 3 + Vite&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="err">#</span><span class="nx">app</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">font</span><span class="o">-</span><span class="nx">family</span><span class="o">:</span> <span class="nx">Avenir</span><span class="p">,</span> <span class="nx">Helvetica</span><span class="p">,</span> <span class="nx">Arial</span><span class="p">,</span> <span class="nx">sans</span><span class="o">-</span><span class="nx">serif</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="o">-</span><span class="nx">webkit</span><span class="o">-</span><span class="nx">font</span><span class="o">-</span><span class="nx">smoothing</span><span class="o">:</span> <span class="nx">antialiased</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="o">-</span><span class="nx">moz</span><span class="o">-</span><span class="nx">osx</span><span class="o">-</span><span class="nx">font</span><span class="o">-</span><span class="nx">smoothing</span><span class="o">:</span> <span class="nx">grayscale</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">-</span><span class="nx">align</span><span class="o">:</span> <span class="nx">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">color</span><span class="o">:</span> <span class="err">#</span><span class="mi">2</span><span class="nx">c3e50</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">margin</span><span class="o">-</span><span class="nx">top</span><span class="o">:</span> <span class="mi">60</span><span class="nx">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现在在命令行中运行<code>yarn dev</code>,再打开浏览器查看<code>http://localhost:3000</code>页面,是不是发现,哎?!居然引入了!(心中狂喜,要早日实现下班自由了)</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gvtvbpyfmmj31bc0u0gn2.jpg" alt="截屏2021-10-27 下午2.47.55" style="zoom:50%;" /> +<h3 id="自动引入ui库组件">自动引入UI库组件</h3> +<p>这里以element plus为例。</p> +<p>首先是安装<code>element plus</code>。官方文档也没说要装,我还以为内置呢,一直报错,有点懵逼,哈哈。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D element-plus +</span></span></code></pre></td></tr></table> +</div> +</div><p>引入element plus的resolver,此处编辑vite.config.js文件,修改后如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Components</span> <span class="nx">from</span> <span class="s1">&#39;unplugin-vue-components/vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ElementPlusResolver</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;unplugin-vue-components/resolvers&#39;</span> <span class="c1">// 引入ElementPlusResolver +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="nx">vue</span><span class="p">(),</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Components</span><span class="p">({</span><span class="nx">resolvers</span><span class="o">:</span> <span class="p">[</span><span class="nx">ElementPlusResolver</span><span class="p">()]})</span> <span class="c1">// 添加配置 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>那么现在神奇的事情来了,就可以直接使用UI库的组件了!</p> +<p>我们在<code>App.vue</code>中使用一个<code>el-button</code>组件试试。我们在<code>App.vue</code>中加入如下行:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">el-button</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span><span class="nx">Kuari</span><span class="o">&lt;</span><span class="err">/el-button&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>添加后<code>App.vue</code>上下代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Vue logo&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./assets/logo.png&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hello Vue 3 + Vite&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">el-button</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span><span class="nx">Kuari</span><span class="o">&lt;</span><span class="err">/el-button&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现在,运行<code>yarn dev</code>,打开浏览器,可以看到,就直接可以使用UI库的组件了。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gvtvnf0wwij31bc0u0ta7.jpg" alt="截屏2021-10-27 下午2.59.06" style="zoom:50%;" /> +<h3 id="打包">打包</h3> +<p>按需引入的功能并不是仅仅在开发时候的,在打包时,该组件也是Tree-shakable的,只会将你用了的组件打包。</p> +<p>按照当前教程所写的项目,当全局引入的时候,打包的dist文件夹为1.1MB,而使用该组件之后打包,其dist文件夹为202KB。</p> +<h2 id="最后">最后</h2> +<p>手动按需导入是不可能手动按需导入的,这辈子都不可能了/狗头。</p> +<p>毕竟是大佬开发的强力工具,希望前端小伙伴儿们早日实现下班自由。</p> + + + + Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + Mon, 25 Oct 2021 16:07:10 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + <h2 id="简介">简介</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a>中,我们了解了如何使用<code>Vite</code>和<code>Electron</code>来快速构建一个Vue3桌面应用。但是,之前构建的应用仅仅是一个简单的版本。在开发过程中,为了更好的开发体验,在开发electron的时候,肯定也希望能有动态模块热重载(HMR),更别说vite那迅雷不及掩耳盗铃儿响叮当之势的加载速度。</p> +<p>因此,接着上一篇文章所完成的项目代码,我们来完成<code>Vite</code>和<code>Electron</code>开发时的动态模块热重载功能。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,可利用electron中的<code>mainWindow.loadURL(&lt;your-url&gt;)</code>来实现。</p> +<p>对于动态模块热重载功能来说,无论是webpack还是vite,其都是将构建内容存入内存,因此我们无法使用<code>mainWindow.loadFile('dist/index.html')</code>这样加载文件的方式。</p> +<p>但是,单纯地改变该配置也是不行的,需要使用vite将开发服务器运行起来,可以正常运行动态模块热重载,而electron直接加载其开发服务器可访问的url,即<code>http://localhost:3000</code>。</p> +<h2 id="实现步骤">实现步骤</h2> +<h3 id="编辑mainjs">编辑main.js</h3> +<p>将<code>mainWindow.loadFile('dist/index.html')</code>更新为<code>mainWindow.loadURL(&quot;http://localhost:3000&quot;)</code>,更新后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span><span class="s2">&#34;http://localhost:3000&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑viteconfigjs">编辑vite.config.js</h3> +<p>修改文件<code>vite.config.js</code>的<code>base</code>,修改后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// vite.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="s2">&#34;./&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="同时开启vite和electron服务">同时开启vite和electron服务</h3> +<p>为了使vite和electron正常运行,需要先运行vite,使得其开发服务器的url可以正常访问,然后再开启electron去加载url。</p> +<p>此处需要安装两个库:</p> +<ul> +<li><strong>concurrently</strong>:阻塞运行多个命令,<code>-k</code>参数用来清除其它已经存在或者挂掉的进程</li> +<li><strong>wait-on</strong>:等待资源,此处用来等待url可访问</li> +</ul> +<p>首先来安装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D concurrently wait-on +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着更新文件<code>package.json</code>,<code>scripts</code>新增两条命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"> <span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>现已添加两条命令:</p> +<ul> +<li><code>yarn electron</code>为等待tcp协议3000端口可访问,然后执行electron</li> +<li><code>yarn electron:serve</code>为阻塞执行开发服务器运行和<code>yarn electron</code>命令</li> +</ul> +<p>运行项目只要执行命令<code>yarn electron:serve</code>即可,当修改项目文件时,桌面应用也将自动更新。</p> +<h2 id="参考文件">参考文件</h2> +<ul> +<li><a class="link" href="https://cn.vitejs.dev/guide/why.html#slow-server-start" target="_blank" rel="noopener" + >为什么选vite</a></li> +<li><a class="link" href="https://dev.to/brojenuel/vite-vue-3-electron-5h4o" target="_blank" rel="noopener" + >vite+vue3+electron+typescript</a></li> +</ul> + + + + Golang如何使用validator校验参数 + https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/ + Sat, 23 Oct 2021 15:31:19 +0000 + + https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/ + <h2 id="简介">简介</h2> +<p>关于测试工程师,有一个笑话,是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯咖啡 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了0.7杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了-1杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了2^32杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯洗脚水 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯蜥蜴 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一份asdfQwer@24dg!&amp;*(@ +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,什么也没要 +</span></span><span class="line"><span class="cl">一个测试工程师走进家酒吧,又走出去又从窗户进来又从后门出去从下水道钻进来 +</span></span><span class="line"><span class="cl">一个测试工程师走进家酒吧,又走出去又进来又出去又进来又出去,最后在外面把老板打了一顿 +</span></span><span class="line"><span class="cl">一个测试工程师走进一 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯烫烫烫的锟斤拷 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了NaN杯Null +</span></span><span class="line"><span class="cl">1T测试工程师冲进一家酒吧,要了500T啤酒咖啡洗脚水野猫狼牙棒奶茶 +</span></span><span class="line"><span class="cl">1T测试工程师把酒吧拆了 +</span></span><span class="line"><span class="cl">一个测试工程师化装成老板走进一家酒吧,要了500杯啤酒,并且不付钱 +</span></span><span class="line"><span class="cl">一万个测试工程师在酒吧外呼啸而过 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯啤酒‘;DROPTABLE酒吧 +</span></span><span class="line"><span class="cl">测试工程师们满意地离开了酒吧 +</span></span></code></pre></td></tr></table> +</div> +</div><p>这个笑话估计也只有开发才明白其中的笑点与心酸吧。</p> +<p>对于一些刚入门的开发来说,这简直就是噩梦。当初刚入门的时候我的代码也是很多毛病,经不起这样的测试,后来渐渐地经验多了后,代码的健壮性逐渐提升,也明白其中比较重要的就是参数的校验。</p> +<p>参数的使用有通过协议的接口调用(如http、rpc&hellip;&hellip;)、函数调用、库调用等等方式。</p> +<p>其实对于http api的请求来说,现在很多web框架都已经自带了参数校验的功能,基本用起来都挺爽的,也无需多讲。</p> +<p>而对于函数调用这样的常见方式,很多是要靠开发自己去校验参数的。如果仅仅是靠注释,在团队开发过程中,难免会有问题产生。起码我觉得,永远不要相信传过来的参数!</p> +<p>OK,那么来讲讲这次的题目,就是Golang中的参数校验库——validator。</p> +<p>用过Gin的小伙伴儿应该知道其<code>binding</code>参数验证器非常好用,其就是调用了validator。此处呢我们来介绍下validator的基础用法,和在一般场景下的应用案例。</p> +<h2 id="基础用法">基础用法</h2> +<h3 id="介绍">介绍</h3> +<p><code>validator</code>包源码在<a class="link" href="https://github.com/go-playground/validator" target="_blank" rel="noopener" + >github.com/go-playground/validator</a>。其基于标记实现结构和单个字段的值验证,包含如下关键功能:</p> +<ul> +<li>使用验证标记或自定义验证程序进行跨字段和跨结构验证</li> +<li>Slice、Array和Map都可以允许验证多维字段的任何或者所有级别</li> +<li>能够深入查看映射键和值以进行验证</li> +<li>通过在验证之前确定类型接口的基础类型来处理类型接口</li> +<li>处理自定义字段类型</li> +<li>允许将多个验证映射到单个标记,以便在结构上更轻松地定义验证</li> +<li>提取自定义定义的字段名,例如,可以指定在验证时提取JSON名称,并使其在结果FieldError中可用</li> +<li>可定制的i18n错误消息</li> +<li>gin web框架的默认验证器</li> +</ul> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go get github.com/go-playground/validator/v10 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="导入">导入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;github.com/go-playground/validator/v10&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证规则">验证规则</h3> +<p>此处从官方列举的各个类别中挑选部分举例说明。</p> +<h4 id="1比较">1)比较</h4> +<ul> +<li><strong>eq</strong>:相等</li> +<li><strong>gt</strong>:大于</li> +<li><strong>gte</strong>:大于等于</li> +<li><strong>lt</strong>:小于</li> +<li><strong>lte</strong>:小于等于</li> +<li><strong>ne</strong>:不等于</li> +</ul> +<h4 id="2字段">2)字段</h4> +<p>此处的字段大部分可以理解为上面的比较的tag跟<code>field</code>拼接而成,而中间有<code>cs</code>的tag为跨struct比较。</p> +<ul> +<li><strong>eqfield</strong>(=Field):必须等于Field的值</li> +<li><strong>nefield</strong>(=Field):必须不等于Field的值</li> +<li><strong>gtfield</strong>(=Field):必须大于Field的值</li> +<li><strong>eqcsfield</strong>(=Other.Field):必须等于struct Other中的Field的值</li> +</ul> +<h4 id="3网络">3)网络</h4> +<ul> +<li><strong>ip</strong>:网络协议地址IP</li> +<li><strong>ip4_addr</strong>:网络协议地址IPv4</li> +<li><strong>mac</strong>:mac地址</li> +<li><strong>url</strong>:url</li> +</ul> +<h4 id="4字符">4)字符</h4> +<ul> +<li><strong>ascii</strong>:ASCII</li> +<li><strong>boolean</strong>:Boolean</li> +<li><strong>endswith</strong>: 以&hellip;结尾</li> +<li><strong>contains</strong>:包含</li> +<li><strong>uppercase</strong>:大写</li> +</ul> +<h4 id="5格式">5)格式</h4> +<ul> +<li><strong>base64</strong>:Base64字符串</li> +<li><strong>base64url</strong>:Base64url字符串</li> +<li><strong>email</strong>:邮箱字符串</li> +<li><strong>json</strong>:JSON</li> +<li><strong>jwt</strong>:JSON Web Token</li> +<li><strong>latitude</strong>:纬度</li> +</ul> +<h4 id="6其它">6)其它</h4> +<ul> +<li><strong>len</strong>:长度</li> +<li><strong>max</strong>:最大值</li> +<li><strong>min</strong>:最小值</li> +<li><strong>required</strong>:字段为必须,不可空</li> +</ul> +<h4 id="7别名">7)别名</h4> +<ul> +<li><strong>iscolor</strong>:hexcolor|rgb|rgba|hsl|hsla</li> +<li><strong>country_code</strong>:iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric</li> +</ul> +<h2 id="案例">案例</h2> +<h3 id="简单验证">简单验证</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-playground/validator/v10&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,lte=10&#34;`</span> <span class="c1">// 姓名 非空,长度小于等于10 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Age</span> <span class="kt">int</span> <span class="s">`validate:&#34;required,gte=18,lte=50&#34;`</span> <span class="c1">// 年龄 非空,数字大于等于18,小于等于50 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Email</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,email&#34;`</span> <span class="c1">// 邮箱 非空,格式为email +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">FavouriteColor</span> <span class="kt">string</span> <span class="s">`validate:&#34;iscolor&#34;`</span> <span class="c1">// 喜欢的颜色 hexcolor|rgb|rgba|hsl|hsla的别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Password</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,gte=16,lte=22&#34;`</span> <span class="c1">// 密码 非空,长度大于等于16,小于等于22 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">RePassword</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,gte=16,lte=22,eqfield=Password&#34;`</span> <span class="c1">// 确认密码 非空,长度大于等于16,小于等于22,必须和字段Password相同 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Hobbies</span> <span class="p">[]</span><span class="nx">Hobby</span> <span class="s">`validate:&#34;lte=5&#34;`</span> <span class="c1">// 多个爱好 长度小于等于5 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Hobby</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> <span class="s">`validate:&#34;lte=50&#34;`</span> <span class="c1">// 爱好名称 长度小于等于50 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">validate</span> <span class="o">*</span><span class="nx">validator</span><span class="p">.</span><span class="nx">Validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 该函数验证struct +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 不会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">validateStruct</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 该函数单度验证字段 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">validateVariable</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">validateStruct</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">hobby</span> <span class="o">:=</span> <span class="nx">Hobby</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;划水&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;张三&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Age</span><span class="p">:</span> <span class="mi">48</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Email</span><span class="p">:</span> <span class="s">&#34;hi.hunterji@gmail.com&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">FavouriteColor</span><span class="p">:</span> <span class="s">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="s">&#34;1234567890123456&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">RePassword</span><span class="p">:</span> <span class="s">&#34;1234567890123456&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Hobbies</span><span class="p">:</span> <span class="p">[]</span><span class="nx">Hobby</span><span class="p">{</span><span class="nx">hobby</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Struct</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">validateVariable</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">email</span> <span class="o">:=</span> <span class="s">&#34;hi.hunterji@gmail.com&#34;</span> <span class="c1">// 此处邮箱地址格式写的是错误的,会导致报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">email</span><span class="p">,</span> <span class="s">&#34;required,email&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义验证">自定义验证</h3> +<p>自定义验证可以自己创建一个校验的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 注册校验函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">ValidateMyVal</span><span class="p">(</span><span class="nx">fl</span> <span class="nx">validator</span><span class="p">.</span><span class="nx">FieldLevel</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fl</span><span class="p">.</span><span class="nf">Field</span><span class="p">().</span><span class="nf">String</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;hello,world!&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后将其注册到validate上即可:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">validate</span><span class="p">.</span><span class="nf">RegisterValidation</span><span class="p">(</span><span class="s">&#34;is-hello&#34;</span><span class="p">,</span> <span class="nx">ValidateMyVal</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">s</span> <span class="o">:=</span> <span class="s">&#34;hello,kuari&#34;</span> <span class="c1">// 跟校验函数中的字符串不同,因此此处会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="s">&#34;is-hello&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>自定义校验可以满足开发过程中的特殊场景,通过制定规范的校验标准,可以推进团队的协作和开发效率。</p> +<h2 id="最后">最后</h2> +<p>至此便是对于<code>validator</code>的介绍了。本文篇幅较短,管中窥豹而已,基本可以满足简单场景的使用。以及本文的案例也是基于官方的案例改的,让其稍微接地气点。若有兴趣的小伙伴还是建议去完整看一下官方的文档和案例,多样的用法可以满足多样的场景,在满足代码的健壮性的同时也能确保代码的优美。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/go-playground/validator" target="_blank" rel="noopener" + >go-playground/validator</a></li> +</ul> + + + + Vite+Electron快速构建一个VUE3桌面应用(三)——打包 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + Tue, 19 Oct 2021 15:26:46 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + <h2 id="简介">简介</h2> +<p>上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a>完成了开发时的动态模块热重载功能,现在是时候来看看怎么完成最后一步——打包了。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,重点还是在于<code>mainWindow.loadURL()</code>。</p> +<p>打包后还是加载<code>http://localhost:3000</code>是无法运行的,因此,此处需要先用vite打包好,然后使用<code>electron-builder</code>加载vite打包后的文件进行打包。</p> +<p>为了代码能够根据不同环境在运行时加载<code>http://localhost:3000</code>,在打包时加载文件,此处需要使用环境变量来切换生产和开发环境。</p> +<h2 id="实现">实现</h2> +<h3 id="环境变量">环境变量</h3> +<p>此处使用环境变量<code>NODE_ENV</code>来切换生产和开发环境,生产环境为<code>NODE_ENV=production</code>,开发环境为<code>NODE_ENV=development</code>,若有其它如<code>release</code>等环境可在此基础上拓展。</p> +<h3 id="创建electron文件夹">创建electron文件夹</h3> +<p>在项目根目录下创建文件夹<code>electron</code>,将<code>main.js</code>和<code>preload.js</code>文件移动进来。其结构如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── electron +</span></span><span class="line"><span class="cl">│   ├── main.js +</span></span><span class="line"><span class="cl">│   └── preload.js +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>若还是不太明白可以看看<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >源码</a>中文件结构。</p> +<h3 id="编辑electronmainjs">编辑electron/main.js</h3> +<p>该文件主要是需要根据环境变量切换electron加载的内容,修改内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>修改后的完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// electron/main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">NODE_ENV</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s2">&#34;development&#34;</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">webContents</span><span class="p">.</span><span class="nx">openDevTools</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑packagejson">编辑package.json</h3> +<p>首先修改<code>main </code>属性,将<code>main: main.js</code>改为<code>main: electron/main.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="err">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,编辑<code>build</code>属性:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;build&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.your-website.your-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;ElectronApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 &lt;your-name&gt;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,更新<code>scripts</code>属性。</p> +<p>此处需要先安装两个库:</p> +<ul> +<li><strong><code>cross-env</code></strong>: 该库让开发者只需要注重环境变量的设置,而无需担心平台设置</li> +<li><strong><code>electron-builder</code></strong>: electron打包库</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D cross-env electron-builder +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后的<code>scripts</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> <span class="c1">// 此处需要设置环境变量以保证开发时加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> <span class="c1">// 新增打包命令 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,更新后的<code>package.json</code>完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron-builder&#34;</span><span class="p">:</span> <span class="s2">&#34;^22.13.1&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.my-website.my-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;MyApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="打包">打包</h2> +<p>直接执行打包命令即可开始打包。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:build +</span></span></code></pre></td></tr></table> +</div> +</div><p>打包完成之后,会多出两个文件夹<code>dist</code>和<code>dist_electron</code>,其文件结构如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── dist +</span></span><span class="line"><span class="cl">│   ├── assets +</span></span><span class="line"><span class="cl">│   ├── favicon.ico +</span></span><span class="line"><span class="cl">│   └── index.html +</span></span><span class="line"><span class="cl">├── dist_electron +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip.blockmap +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg.blockmap +</span></span><span class="line"><span class="cl">│   ├── builder-debug.yml +</span></span><span class="line"><span class="cl">│   ├── builder-effective-config.yaml +</span></span><span class="line"><span class="cl">│   └── mac +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>至此,便完成了打包。</p> +<p>后面再来写写关于electron的优化,减少electron打包后应用的体积。(这玩意儿确实打包下来有点大呢/狗头)</p> + + + + Vite+Electron快速构建一个VUE3桌面应用 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + Mon, 18 Oct 2021 09:58:57 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + <h2 id="简介">简介</h2> +<p>首先,介绍下<code>vite</code>和<code>Electron</code>。</p> +<ul> +<li>Vite是一种新型前端构建工具,能够显著提升前端开发体验。由尤大推出,其发动态表示“再也回不去webpack了&hellip;”</li> +<li>Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。</li> +</ul> +<p>当开始想用vue去开发一个桌面应用时,首先去搜索下,了解到当前如下两种现成方案:</p> +<ul> +<li><strong>electron-vue</strong>: 该项目集成度较好,封装较为完整,中文搜索下来文章较多也是该方案,可以直接上手去使用。但是,问题在于其内置electron的版本太低,写文章时看到的版本是2.0.4,而最新的electron版本是15.1.2。</li> +<li><strong>Vue CLI Plugin Electron Builder</strong>: 该方案是集成到到<code>vue-cli</code>中使用,使用<code>vue add electron-builder</code>后可直接上手,免去了基础配置的步骤。但是其只能在<code>vue-cli</code>下使用,无法配合<code>vite</code>来使用。</li> +</ul> +<p>因此,若要使用<code>vite</code>和<code>electron</code>,还需要自己来配置。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1</a></p> +<h2 id="创建一个vite项目">创建一个Vite项目</h2> +<h3 id="安装-vite">安装 vite</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建项目">创建项目</h3> +<p>创建命令如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite &lt;your-vue-app-name&gt; --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处创建一个项目,名为kuari。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite kuari --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="进入且运行">进入且运行</h3> +<p>进入项目,在运行前需要先安装下依赖。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> kuari +</span></span><span class="line"><span class="cl">yarn install +</span></span><span class="line"><span class="cl">yarn dev +</span></span></code></pre></td></tr></table> +</div> +</div><p>在运行命令敲下的一瞬间,几乎是已经在运行了,不愧是vite。此时按照输出,打开地址预览,即可看到初始化页面。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gveqwmm4eij61bc0u03zy02.jpg" alt="截屏2021-10-14 下午12.50.48" style="zoom:50%;" /> +<p>至此一个基础的vite项目创建完成。</p> +<h2 id="配置electron">配置Electron</h2> +<h3 id="官方文档">官方文档</h3> +<p>在<a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网的快速入门文档</a>中,有官方给出的利用html、javascript、css来创建一个electron应用的案例,vite+electron的方案也借鉴其中。</p> +<h3 id="安装">安装</h3> +<p>首先安装electron至vite应用。目前electron的版本为<code>^15.1.2,</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev electron +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置文件">配置文件</h3> +<p>####config.js</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">path</span> <span class="nx">from</span> <span class="s1">&#39;path&#39;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;./dist/&#39;</span><span class="p">),</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>main.js</code>,需要注意的是,该内容中<code>index.html</code>的加载路径跟electron官网给的配置不同。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadFile</span><span class="p">(</span><span class="s1">&#39;dist/index.html&#39;</span><span class="p">)</span> <span class="c1">// 此处跟electron官网路径不同,需要注意 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>preload.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// preload.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 所有Node.js API都可以在预加载过程中使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 它拥有与Chrome扩展一样的沙盒。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;DOMContentLoaded&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">replaceText</span> <span class="o">=</span> <span class="p">(</span><span class="nx">selector</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="nx">element</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="nx">text</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">dependency</span> <span class="k">of</span> <span class="p">[</span><span class="s1">&#39;chrome&#39;</span><span class="p">,</span> <span class="s1">&#39;node&#39;</span><span class="p">,</span> <span class="s1">&#39;electron&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">replaceText</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">dependency</span><span class="si">}</span><span class="sb">-version`</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">versions</span><span class="p">[</span><span class="nx">dependency</span><span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####json</p> +<p>为了确保能够运行相关electron的命令,需要修改<code>package.json</code>文件。</p> +<p>首先需要去设置<code>main</code>属性,electron默认会去在开始时寻找项目根目录下的<code>index.js</code>文件,此处我们使用的是<code>main.js</code>,所以需要去定义下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后我们需要新增electron的运行命令。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;electron .&#34;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>直接在终端输入如下命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:serve +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着我们就可以看到我们桌面应用就出来咯!</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gves3xrayzj612f0u0myy02.jpg" alt="截屏2021-10-14 下午1.32.38" style="zoom:50%;" /> +<h2 id="最后">最后</h2> +<p>之前做项目一直用的Vue CLI Plugin Electron Builder,这次有个项目先用electron开发一下,推一波看看,后期看情况swift重新开发一个mac的桌面应用。也刚好尝尝鲜,一直没有机会试试vite。</p> +<p>electron这个东东确实很方便,就是打包出来的应用体积太大,真的是硬伤啊。这次目标人群首先是windows用户,所以上electron吧!</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网快速入门</a></li> +<li><a class="link" href="https://vitejs.dev/" target="_blank" rel="noopener" + >Vite官网</a></li> +<li><a class="link" href="https://learnvue.co/2021/05/build-vue-3-desktop-apps-in-just-5-minutes-vite-electron-quick-start-guide/" target="_blank" rel="noopener" + >Build vue3 desktop apps in just 5 minutes</a></li> +</ul> + + + + OSS花式解锁下载文件新姿势,你学废了吗? + https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/ + Wed, 29 Sep 2021 09:22:22 +0000 + + https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/ + <h2 id="简介">简介</h2> +<p>在上一篇文章——<a class="link" href="https://mp.weixin.qq.com/s/qpSWVymlWtwlb5WUd_1M7Q" target="_blank" rel="noopener" + >前端文件花式直传OSS!后端:那我走?</a>中聊了下文件上传的几种方案,这里我们再来聊一下文件下载的花式姿势。</p> +<h2 id="精简版">精简版</h2> +<p>最常见的方式,莫过于后端存储文件在服务器上,然后通过后端接口传给前端,如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwoojpvg6j60lq068jrg02.jpg" + + + + loading="lazy" + + alt="最简版" + + +></p> +<p>该方案对于小规模、成本较低的项目非常适用,开发也较为便捷。</p> +<p>而对于有性能要求的项目,可以通过砸钱加机器、分片下载等方案提升项目性能。如果可以的话,请砸钱加机器吧!</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwq8edesqj60hq0bc75c02.jpg" alt="截屏2021-09-28 下午9.58.10" style="zoom:33%;" /> +<h2 id="中庸版">中庸版</h2> +<p>相较于上一个方案,可以砸丢丢钱整个OSS,将文件存储在OSS上,毕竟OSS上行流量不收费,如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwpuow9hnj60km0dc74n02.jpg" + + + + loading="lazy" + + alt="中庸版直传" + + +></p> +<p>那么问题来了,OSS的下行流量不是收费的吗?!</p> +<p>OK,偷偷告诉各位一个省钱小妙招/狗头,OSS内网的下行流量是不收费的!因此,可以通过后端请求OSS,获取到文件/字符串后,将其以文件流/base64数据的方式返回给前端。这样就避免了下行流量的费用。如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwp05f04jj60r3068glu02.jpg" + + + + loading="lazy" + + alt="中庸版" + + +></p> +<p>不过问题又来了,这样就还是占用了后端服务器的资源,依然会是性能的一个瓶颈。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwq8of8qcj6076075dft02.jpg" alt="截屏2021-09-28 下午10.12.06" style="zoom:50%;" /> +<h2 id="性能版">性能版</h2> +<p>基于上一个方案,可以再升级。砸丢丢钱,拉上CDN这老哥,利用CDN流量代替OSS的下行流量,既能让前端直接请求OSS资源,不占用服务器资源,也降低了成本。如图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwplrnpu2j60r20dc3z102.jpg" + + + + loading="lazy" + + alt="性能版" + + +></p> +<p>在优先性能的情况下,该方案是较优的。</p> +<p>要说缺点的话,就是CDN配置吧,需要买域名和SSL证书等,不过一次购买,后续使用体验会非常棒。CDN除了可以代替OSS的下行流量外,其优点不要太多,比如说CDN可以文件缓存、可以调度至加速节点等。</p> +<p>涉及到OSS的私有Bucket的话,只需要使用CDN的访问控制即可。其也只需要通过后端实现加密,生成文件访问URL给前端直接访问。</p> +<p>或许你会存在疑问,看上去挺麻烦的啊!但是看看CDN的价格,你肯定会有不一样的想法的。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gux8jydxbdj608v09agly02.jpg" alt="截屏2021-09-28 下午10.37.14" style="zoom:50%;" /> + + + 前端文件花式直传OSS!后端:那我走? + https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/ + Tue, 28 Sep 2021 09:53:18 +0000 + + https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/ + <h2 id="简介">简介</h2> +<p>前端还在传文件给后端吗?你们的服务器扛得住吗?什么&hellip;&hellip;老板砸钱加机器?!告辞!/狗头</p> +<p>前后端文件传输涉及数据较大,往往会成为很多项目的性能瓶颈。常见的传输方式也有不少,相对来说,OSS直传能够减轻很大压力。</p> +<p>本文我们来列举下常见的和oss直传的几种传输方式,并列举其优劣。</p> +<h2 id="常见方式">常见方式</h2> +<h3 id="表单上传">表单上传</h3> +<p>表单上传文件是最常见的方式,前后端开发小伙伴都很轻松,前端哐哐传,后端哐哐收就成了。其过程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guw3hw7k3uj60ix06q74b02.jpg" + + + + loading="lazy" + + alt="form上传" + + +></p> +<p><strong>优势:</strong></p> +<ul> +<li>简单方便,开发量小</li> +<li>前后端原生支持,无需额外第三方库支持</li> +</ul> +<h3 id="base64上传">Base64上传</h3> +<p>Base64方式上传文件,多常见于小文件,如小图片等,前后端都可直接使用<code>String</code>类型发送和接收。不过在前端,需要将文件转成base64数据,不仅会增加些性能消耗,还会增加传输数据的体积。而对于后端,如果并不是想直接存储base64数据,也还需要将其转成文件再存储,也会增加后端的性能消耗。</p> +<p>该上传方式可适用于<code>Resetful Api</code>,也可适用于文件的加密、回调接口携带文件等等。其过程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvkwpx4d9j60ix06q74d02.jpg" + + + + loading="lazy" + + alt="base64上传" + + +></p> +<p><strong>优势:</strong></p> +<ul> +<li>适用于<code>Resetful Api</code>,可用于加密、回调等场景,较为灵活</li> +</ul> +<p><strong>劣势:</strong></p> +<ul> +<li>前后端文件与base64数据转换需要消耗性能</li> +<li>只适用于小文件</li> +</ul> +<h2 id="oss直传">OSS直传</h2> +<p>此处的OSS直传方案都是使用的阿里云的OSS产品,以下将介绍三个方案,可适用于不同的场景。</p> +<h3 id="browserjs-sdk上传">Browser.js SDK上传</h3> +<p>该方案可在前端直接通过<code>browser.js</code>上传文件到OSS,可分成三步:</p> +<ol> +<li>前端使用SDK直传OSS</li> +<li>前端上传完成后请求后端,通知上传完成</li> +<li>后端检测OSS上该文件是否存在(可选)</li> +</ol> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvlvyap7zj60lc0crjru02.jpg" + + + + loading="lazy" + + alt="browser直传" + + +></p> +<p><code>Browser.js</code>的方式需要前端安装阿里云的库<code>ali-oss</code>,然后在前端调用。直传还需要OSS账户的Key和Secret,因此为了安全考虑,需要建立RAM账户,然后前端向后端先请求一个<code>STS</code>临时访问凭证来完成直传,其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvlvuqm2bj60lc0cr0tb02.jpg" + + + + loading="lazy" + + alt="browser直传2" + + +></p> +<h3 id="javascript客户端签名直传">Javascript客户端签名直传</h3> +<p>Javascript客户端签名直传,需要先从后端获取临时签名,其流程与上一步的<code>browser.js</code>方案大致相同,不同点在于:</p> +<ul> +<li>无需第三方库支持,直接表单上传</li> +<li>原生支持后端上传回调(下一步骤讲述)</li> +</ul> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvm1snim8j60lc0crgm202.jpg" + + + + loading="lazy" + + alt="javascript签名直传" + + +></p> +<p>不过该方案做下来,我感觉最大的问题是权限配置有点麻烦&hellip;&hellip;/泪眼</p> +<h3 id="服务端签名直传并设置上传回调">服务端签名直传并设置上传回调</h3> +<p>该方案其实是上面方案——Javascript客户端签名直传的升级版本,其加上了后端的上传回调功能。不错呦~</p> +<p>前端需要改动的很少,只需要在请求参数中加上<code>callback</code>参数即可,该参数为后端加密,在签名请求的响应中一起返回回来,内加密了后端回调接口。在前端直传完成后,后端回调接口将会接收到相关文件参数,包括文件路径、大小、类型等。最后OSS会将回调接口response转发给前端,响应直传OSS的请求。</p> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvmceaql3j60lc0craak02.jpg" + + + + loading="lazy" + + alt="后端签名直传且回调" + + +></p> +<h2 id="对比">对比</h2> +<p>传统方式相比直传OSS,相对来说有三个缺点:</p> +<ul> +<li>上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。</li> +<li>扩展性差:如果后续用户多了,应用服务器会成为瓶颈。</li> +<li>费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。</li> +</ul> +<p>当然,对于规模较小、成本较低的项目来说,常见的上传方式还是适合的,毕竟没有最好的,只有最适合的。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/112718.html?spm=5176.22414175.sslink.2.3fc92aca2CzF4n" target="_blank" rel="noopener" + >Web端上传介绍</a></p> +</li> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/31925.html" target="_blank" rel="noopener" + >JavaScript客户端签名直传</a></p> +</li> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/267439.html?spm=a2c4g.11186623.0.0.13415d3fbA6bxA" target="_blank" rel="noopener" + >服务端签名直传并设置上传回调</a></p> +</li> +</ul> + + + + Golang实现农历转换阳历 + https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/ + Tue, 31 Aug 2021 09:08:57 +0000 + + https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/ + <h2 id="前言">前言</h2> +<p>因为项目需求,需要去检测用户的农历生日。虽然后来找到了合适的库,但是首先先解释下<code>农历</code>的定义,也是去了解才知道,原来农历不是阴历。</p> +<p>农历属于阴阳合历,其年份分为平年和闰年。平年为十二个月,闰年为十三个月。月份分为大月和小月,大月三十天,小月二十九天,其平均历月等于一个朔望月。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Go 1.16</li> +<li>github.com/nosixtools/solarlunar 0.0.0</li> +</ul> +<h2 id="库">库</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">nosixtools</span><span class="o">/</span><span class="nx">solarlunar</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>该库支持1900~2049年。所以项目要跑到2049年后的童鞋就要注意&hellip;&hellip;</p> +<p>当然,该库还支持阳历转农历、节假日计算等,有兴趣大家可以自行去了解下。</p> +<h2 id="使用">使用</h2> +<h3 id="判断闰年">判断闰年</h3> +<p>该库不支持闰年判断,所以需要自己去实现闰年的判断,其参数类型为<code>Boolean</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">year</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">result</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">year</span><span class="o">%</span><span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">year</span><span class="o">%</span><span class="mi">100</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">year</span><span class="o">%</span><span class="mi">400</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="转换">转换</h3> +<p>需要转换的阳历日期格式是固定的,是<code>2006-01-02</code>。此处以农历<code>2021-07-17</code>为例。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lunarDate</span> <span class="o">:=</span> <span class="s">&#34;2021-07-17&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">solarlunar</span><span class="p">.</span><span class="nf">LunarToSolar</span><span class="p">(</span><span class="nx">lunarDate</span><span class="p">,</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Year</span><span class="p">())))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>输出为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="mi">2021</span><span class="o">-</span><span class="mi">08</span><span class="o">-</span><span class="mi">24</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="全部代码">全部代码</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/nosixtools/solarlunar&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lunarDate</span> <span class="o">:=</span> <span class="s">&#34;2021-07-17&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">solarlunar</span><span class="p">.</span><span class="nf">LunarToSolar</span><span class="p">(</span><span class="nx">lunarDate</span><span class="p">,</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Year</span><span class="p">())))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">year</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">result</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">year</span><span class="o">%</span><span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">year</span><span class="o">%</span><span class="mi">100</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">year</span><span class="o">%</span><span class="mi">400</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + IOS监听上下左右滑动手势 + https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/ + Mon, 30 Aug 2021 14:29:02 +0000 + + https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/ + <h2 id="前言">前言</h2> +<p>IOS监听手势使用的方法为<code>UISwipeGestureRecognizer</code>。</p> +<h2 id="添加手势监听">添加手势监听</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">gesture</span> <span class="p">=</span> <span class="n">UISwipeGestureRecognizer</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">addTarget</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="k">#selector</span><span class="p">(</span><span class="n">yourSelector</span><span class="p">(</span><span class="n">gesture</span><span class="p">:)))</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">direction</span> <span class="p">=</span> <span class="p">.</span><span class="kr">left</span> <span class="c1">// .left左滑 .right右滑 .up上滑 .down下滑</span> +</span></span><span class="line"><span class="cl"><span class="kc">self</span><span class="p">.</span><span class="n">addGestureRecognizer</span><span class="p">(</span><span class="n">gesture</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="添加响应事件">添加响应事件</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kr">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">leftPushEvent</span><span class="p">(){</span> +</span></span><span class="line"><span class="cl"> <span class="bp">print</span><span class="p">(</span><span class="s">&#34;响应...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="模板">模板</h2> +<p>把上面的整合起来,基本可以按照这个模板来写。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kr">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">leftPushEvent</span><span class="p">(){</span> +</span></span><span class="line"><span class="cl"> <span class="bp">print</span><span class="p">(</span><span class="s">&#34;响应...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">gesture</span> <span class="p">=</span> <span class="n">UISwipeGestureRecognizer</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">addTarget</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="k">#selector</span><span class="p">(</span><span class="n">leftPushEvent</span><span class="p">(</span><span class="n">gesture</span><span class="p">:)))</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">direction</span> <span class="p">=</span> <span class="p">.</span><span class="kr">left</span> +</span></span><span class="line"><span class="cl"><span class="kc">self</span><span class="p">.</span><span class="n">addGestureRecognizer</span><span class="p">(</span><span class="n">gesture</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://www.jianshu.com/p/30104b1c872d" target="_blank" rel="noopener" + >iOS手势识别&ndash;上下左右滑动</a></li> +</ul> + + + + SwiftUI+Reality开发AR项目解决全屏问题 + https://blog.hunterji.com/p/swiftui-reality%E5%BC%80%E5%8F%91ar%E9%A1%B9%E7%9B%AE%E8%A7%A3%E5%86%B3%E5%85%A8%E5%B1%8F%E9%97%AE%E9%A2%98/ + Mon, 30 Aug 2021 14:11:58 +0000 + + https://blog.hunterji.com/p/swiftui-reality%E5%BC%80%E5%8F%91ar%E9%A1%B9%E7%9B%AE%E8%A7%A3%E5%86%B3%E5%85%A8%E5%B1%8F%E9%97%AE%E9%A2%98/ + <h2 id="环境">环境</h2> +<ul> +<li>Swift 5.4</li> +<li>Xcode 12.5.1</li> +</ul> +<h2 id="问题">问题</h2> +<p>在使用<code>Swift UI</code>和<code>Realiy</code>开发AR项目时,发现摄像头一直是居中的,无法全屏。</p> +<h2 id="解决">解决</h2> +<h3 id="创建launchscreenstoryboard文件">创建<code>LaunchScreen.storyboard</code>文件</h3> +<p>在左侧文件列表中新建文件,名为<code>LaunchScreen.storyboard</code>。</p> +<h3 id="设置launch-screen-file">设置<code>Launch Screen File</code></h3> +<p>点击左侧文件列表中你的项目文件(最顶级文件),进入文件<code>[your-project].xcodeproj</code>文件。</p> +<p>在<code>General</code>中,找到<code>App Icons and Launch Images</code>,在其模块中有<code>Launch Screen File</code>选项,点击选择为<code>LaunchScreen.storyboard</code>。</p> +<p>总结下就是:<code>[yourTarget] -&gt; General -&gt; App Icons and Launch Images</code>。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/56733642/how-to-make-swiftui-view-fullscreen" target="_blank" rel="noopener" + >How to make SwiftUI view fullscreen?</a></li> +</ul> + + + + Interval计时器在tab页切换或者隐藏情况下停止运行 + https://blog.hunterji.com/p/interval%E8%AE%A1%E6%97%B6%E5%99%A8%E5%9C%A8tab%E9%A1%B5%E5%88%87%E6%8D%A2%E6%88%96%E8%80%85%E9%9A%90%E8%97%8F%E6%83%85%E5%86%B5%E4%B8%8B%E5%81%9C%E6%AD%A2%E8%BF%90%E8%A1%8C/ + Wed, 11 Aug 2021 16:20:05 +0000 + + https://blog.hunterji.com/p/interval%E8%AE%A1%E6%97%B6%E5%99%A8%E5%9C%A8tab%E9%A1%B5%E5%88%87%E6%8D%A2%E6%88%96%E8%80%85%E9%9A%90%E8%97%8F%E6%83%85%E5%86%B5%E4%B8%8B%E5%81%9C%E6%AD%A2%E8%BF%90%E8%A1%8C/ + <h2 id="问题">问题</h2> +<p>开始是开发<code>electron</code>时遇到的问题,使用<code>Interval</code>计时器,在窗口最小化隐藏再打开,计时器在隐藏期间并没有工作。</p> +<p>后来网上查询相关问题,发现更多是在浏览器tab页隐藏/切换情况下,计时器就会停止。</p> +<h2 id="解决">解决</h2> +<blockquote> +<p>在后台选项卡上运行的计时器方法可能会耗尽资源。在后台选项卡中以非常短的时间间隔运行回调的应用程序可能会消耗大量内存,以至于当前活动选项卡的工作可能会受到影响。在最坏的情况下,浏览器可能会崩溃,或者设备的电池会很快耗尽。</p> +</blockquote> +<p>此限制是浏览器限制的。</p> +<p>无法突破限制,但是可以使用折中的方式,当然我也觉得此方式相较于一直计时会更优,即监听<code>visibilitychange</code>事件。</p> +<p><code>visibilitychange</code>事件可以监听tab页面的激活与失活事件,因此可以:</p> +<ul> +<li>在失活时,记录计时器计算的最后的值,清空计时器</li> +<li>在激活时,计算失活期间应有的值,继续使用计时器计算</li> +</ul> +<h3 id="添加事件代码如下">添加事件代码如下:</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;visibilitychange&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">hidden</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// tab页失活 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// tab页激活 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li> +<p><a class="link" href="https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event" target="_blank" rel="noopener" + >https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event</a></p> +</li> +<li> +<p><a class="link" href="https://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab" target="_blank" rel="noopener" + >https://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab</a></p> +</li> +</ul> + + + + go中json解析报错invalid character '\\b' after top-level value + https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/ + Wed, 11 Aug 2021 13:38:49 +0000 + + https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/ + <h2 id="报错">报错</h2> +<p>在<code>Golang</code>中<code>json</code>解析时报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">invalid character &#39;\\b&#39; after top-level value +</span></span></code></pre></td></tr></table> +</div> +</div><p>代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">result</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">response</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="分析与排错">分析与排错</h2> +<p>首先将<code>result</code>打印出来,发现并无异常,其标点符号也没有问题。</p> +<p>然后查看网上现有解决方案的帖子基本试了下,起码对于我来说并不适用,概括下方案:</p> +<ol> +<li>遍历然后过滤,最后重组;</li> +<li>遍历,使用SetEscapeHTML(false)禁用转义符;</li> +<li>编码;</li> +<li>&hellip;</li> +</ol> +<p>最后对比代码中获取到的字符产长度和手动复制所见的字符串的长度,发现确实代码中字符长度不同,其长度是80,而手动复制的字符串的长度是72。</p> +<h2 id="解决">解决</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">strings</span><span class="p">.</span><span class="nf">ReplaceAll</span><span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="s">&#34;\b&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>就挺简单的&hellip;&hellip;</p> + + + + Golang AES-256-CBC加密和解密 + https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/ + Wed, 11 Aug 2021 13:13:12 +0000 + + https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/ + <h2 id="前言">前言</h2> +<p>项目开发中遇到该问题,网上的文章太乱,为了节省下次踩坑时间,特此记录。</p> +<h2 id="加解密">加解密</h2> +<h3 id="填充函数">填充函数</h3> +<p>该函数在加解密中都需要用到。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">PKCS5Padding</span><span class="p">(</span><span class="nx">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padding</span> <span class="o">:=</span> <span class="nx">blockSize</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">blockSize</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padText</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="nb">byte</span><span class="p">(</span><span class="nx">padding</span><span class="p">)},</span> <span class="nx">padding</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">padText</span><span class="o">...</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="加密">加密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Ase256Encrypt</span><span class="p">(</span><span class="nx">plaintext</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bKey</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bIV</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bPlaintext</span> <span class="o">:=</span> <span class="nf">PKCS5Padding</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">plaintext</span><span class="p">),</span> <span class="nx">blockSize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">(</span><span class="nx">bKey</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bPlaintext</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCEncrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="nx">bIV</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">bPlaintext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">EncodeToString</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解密">解密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">DecodeString</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;ciphertext is not a multiple of the block size&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCDecrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="全部代码">全部代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 填充 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">PKCS5Padding</span><span class="p">(</span><span class="nx">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padding</span> <span class="o">:=</span> <span class="nx">blockSize</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">blockSize</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padText</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="nb">byte</span><span class="p">(</span><span class="nx">padding</span><span class="p">)},</span> <span class="nx">padding</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">padText</span><span class="o">...</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 加密 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Ase256</span><span class="p">(</span><span class="nx">plaintext</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bKey</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bIV</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bPlaintext</span> <span class="o">:=</span> <span class="nf">PKCS5Padding</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">plaintext</span><span class="p">),</span> <span class="nx">blockSize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">(</span><span class="nx">bKey</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bPlaintext</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCEncrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="nx">bIV</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">bPlaintext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">EncodeToString</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解密 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">DecodeString</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;ciphertext is not a multiple of the block size&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCDecrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="调用">调用</h2> +<h3 id="加密-1">加密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">result</span> <span class="o">:=</span> <span class="nf">Ase256Encrypt</span><span class="p">(</span><span class="s">&#34;&lt;需要加密的数据&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;key&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;iv&gt;&#34;</span><span class="p">,</span> <span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解密-1">解密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">result</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="s">&#34;&lt;需要解密的数据&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;key&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;iv&gt;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + node spawn在windows下不生效问题记录 + https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + Thu, 29 Jul 2021 13:49:13 +0000 + + https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + <h2 id="问题描述">问题描述</h2> +<p>使用<code>electron</code>开发的windows桌面应用程序,在调用目标文件夹底下的exe执行文件时,开发机子上没有问题,但是其他机子使用时一直调用失败,也抓取不到日志。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">spawn</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">remote</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">getAppPath</span><span class="p">(),</span> <span class="s2">&#34;../target.exe&#34;</span><span class="p">),</span> <span class="p">[],</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">shell</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">detached</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">windowsHide</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="原因">原因</h2> +<p><strong>路径存在空格。</strong></p> +<p>也是经过各种原因排查,然后一次偶然的成功才注意到了路径问题,排查之后发现确实是这问题&hellip;&hellip;</p> +<h2 id="解决">解决</h2> +<p><code>spawn</code>按照如上我的代码一定条件下可以运行,其有一个参数<code>cwd</code>,用来表明运行目录。<code>spawn</code>第一个参数必须是命令的名字,不能是路径。</p> +<p>所以如上代码改成这样:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">spawn</span><span class="p">(</span><span class="s2">&#34;target.exe&#34;</span><span class="p">,</span> <span class="p">[],</span> <span class="p">{</span> <span class="c1">// 此处直接写目标exe文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">cwd</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">remote</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">getAppPath</span><span class="p">(),</span> <span class="s2">&#34;../&#34;</span><span class="p">),</span> <span class="c1">// 注意这里,使用了cwd参数来写运行目录 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">shell</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">detached</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">windowsHide</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/21356372/node-child-process-spawn-not-working-with-spaces-in-path-on-windows" target="_blank" rel="noopener" + >node child_process.spawn not working with spaces in path on windows</a></li> +</ul> + + + + SwiftUI MacOS项目alert弹出两次问题解决 + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AEalert%E5%BC%B9%E5%87%BA%E4%B8%A4%E6%AC%A1%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ + Mon, 12 Jul 2021 16:40:57 +0000 + + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AEalert%E5%BC%B9%E5%87%BA%E4%B8%A4%E6%AC%A1%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ + <h2 id="问题">问题</h2> +<p>使用<code>Alert</code>时,将其用在<code>list</code>的循环视图元素中,弹出<code>Alert</code>时,一定时长不选择就会在点击后弹出第二次。</p> +<p>这里提一下就是之前在网上看到一个帖子说他将<code>Alert</code>放在<code>NavigationView</code>上也会出现该问题。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"> <span class="n">VStask</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">,</span> <span class="n">id</span><span class="p">:</span> <span class="err">\</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span> +</span></span><span class="line"><span class="cl"> <span class="n">ElementView</span><span class="p">(</span><span class="n">item</span><span class="p">:</span> <span class="n">item</span><span class="p">)</span> <span class="c1">// 循环中的元素</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">alert</span><span class="p">(</span><span class="n">isPresented</span><span class="p">:</span> <span class="err">$</span><span class="n">showAlert</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Alert</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">title</span><span class="p">:</span> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;删除确认&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">message</span><span class="p">:</span> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;请问您确认删除该数据吗?&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">primaryButton</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;取消&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">showAlert</span> <span class="p">=</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">secondaryButton</span><span class="p">:</span> <span class="p">.</span><span class="n">destructive</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;删除&#34;</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="p">[</span><span class="n">index</span><span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>将<code>Alert</code>放到循环之前的元素上,比如<code>VStack</code>、<code>List</code>。</p> +<h2 id="参考">参考</h2> +<ul> +<li><a class="link" href="https://www.reddit.com/r/swift/comments/oahztb/swiftui_alert_showing_twice_despite_only_setting/" target="_blank" rel="noopener" + >参考帖子</a></li> +</ul> + + + + SwiftUI项目判断是否为暗黑模式 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E6%9A%97%E9%BB%91%E6%A8%A1%E5%BC%8F/ + Tue, 08 Jun 2021 09:20:38 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E6%9A%97%E9%BB%91%E6%A8%A1%E5%BC%8F/ + <h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">colorScheme</span><span class="p">)</span> <span class="kd">var</span> <span class="nv">colorScheme</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">isLight</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">colorScheme</span> <span class="p">==</span> <span class="p">.</span><span class="n">light</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="调用">调用</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Text</span><span class="p">(</span><span class="s">&#34;Hello, World !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">foregroundColor</span><span class="p">(</span><span class="n">isLight</span> <span class="p">?</span> <span class="n">Color</span><span class="p">.</span><span class="n">red</span> <span class="p">:</span> <span class="n">Color</span><span class="p">.</span><span class="n">green</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整例子">完整例子</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">SwiftUI</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">CheckIsLight</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">colorScheme</span><span class="p">)</span> <span class="kd">var</span> <span class="nv">colorScheme</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">isLight</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">colorScheme</span> <span class="p">==</span> <span class="p">.</span><span class="n">light</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Hello, World !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">foregroundColor</span><span class="p">(</span><span class="n">isLight</span> <span class="p">?</span> <span class="n">Color</span><span class="p">.</span><span class="n">red</span> <span class="p">:</span> <span class="n">Color</span><span class="p">.</span><span class="n">green</span><span class="p">)</span> <span class="c1">// 此处使用isLght实现根据暗黑模式切换字体颜色</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">CheckIsLight_Previews</span><span class="p">:</span> <span class="n">PreviewProvider</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">static</span> <span class="kd">var</span> <span class="nv">previews</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">CheckIsLight</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目调用生物识别(Touch ID -- Face ID) + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8%E7%94%9F%E7%89%A9%E8%AF%86%E5%88%ABtouch-id--face-id/ + Mon, 07 Jun 2021 17:00:00 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8%E7%94%9F%E7%89%A9%E8%AF%86%E5%88%ABtouch-id--face-id/ + <h2 id="设置权限">设置权限</h2> +<p>打开文件<code>info.plist</code>,在空白处右击,选择<code>Add Row</code>,输入选择<code>Privacy - Face ID Usage Description</code>,然后在<code>value</code>中写入<code>我们需要验证您的身份以保护数据</code>。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_face_privacy_info_plist.png" + + + + loading="lazy" + + alt="swiftui_face_privacy_info_plist.png" + + +></p> +<h2 id="代码层面接入">代码层面接入</h2> +<p>打开<code>ContentView.swift</code>文件,开始如下操作。</p> +<h3 id="引入相关库">引入相关库</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">LocalAuthentication</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建lock变量">创建lock变量</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">State</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">isUnlocked</span> <span class="p">=</span> <span class="kc">false</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>isUnlocked</code>为是否解锁,<code>true</code>表示验证完成,已解锁,<code>false</code>表示验证失败,未解锁。</p> +<h3 id="创建函数">创建函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">authenticate</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">context</span> <span class="p">=</span> <span class="n">LAContext</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">error</span><span class="p">:</span> <span class="n">NSError</span><span class="p">?</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 检查生物特征认证是否可用</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">context</span><span class="p">.</span><span class="n">canEvaluatePolicy</span><span class="p">(.</span><span class="n">deviceOwnerAuthenticationWithBiometrics</span><span class="p">,</span> <span class="n">error</span><span class="p">:</span> <span class="p">&amp;</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 可用,所以继续使用它</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">reason</span> <span class="p">=</span> <span class="s">&#34;我们需要验证您的身份以保护数据&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">context</span><span class="p">.</span><span class="n">evaluatePolicy</span><span class="p">(.</span><span class="n">deviceOwnerAuthenticationWithBiometrics</span><span class="p">,</span> <span class="n">localizedReason</span><span class="p">:</span> <span class="n">reason</span><span class="p">)</span> <span class="p">{</span> <span class="n">success</span><span class="p">,</span> <span class="n">authenticationError</span> <span class="k">in</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 身份验证现已完成</span> +</span></span><span class="line"><span class="cl"> <span class="n">DispatchQueue</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">async</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">success</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 认证成功,解锁</span> +</span></span><span class="line"><span class="cl"> <span class="kc">self</span><span class="p">.</span><span class="n">isUnlocked</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 发生的异常</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 没有生物识别</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="根据isunlocked切换view">根据isUnlocked切换View</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">VStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="kc">self</span><span class="p">.</span><span class="n">isUnlocked</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Unlocked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Locked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">onAppear</span><span class="p">(</span><span class="n">perform</span><span class="p">:</span> <span class="n">authenticate</span><span class="p">)</span> <span class="c1">// 该方法调用生物识别验证函数</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="审核问题">审核问题</h2> +<p>最近提交了ios和macos两个产品到app store,我设置的强制使用生物识别才能进入应用。但是出现的问题是macos的审核过了,而ios的审核没有过,其反馈的问题即为开始的生物识别没有过,审核人员使用的模拟器,根本不存在生物识别。</p> +<p>所以跟可能会踩这个坑的小伙伴儿提个醒,目前我还没有好的解决方案,正在等待新的审核中&hellip;&hellip;</p> + + + + SwiftUI MacOS项目根据屏幕大小调整窗口大小 + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AE%E6%A0%B9%E6%8D%AE%E5%B1%8F%E5%B9%95%E5%A4%A7%E5%B0%8F%E8%B0%83%E6%95%B4%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F/ + Mon, 07 Jun 2021 16:55:50 +0000 + + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AE%E6%A0%B9%E6%8D%AE%E5%B1%8F%E5%B9%95%E5%A4%A7%E5%B0%8F%E8%B0%83%E6%95%B4%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F/ + <h2 id="代码实现">代码实现</h2> +<h3 id="获取屏幕对象">获取屏幕对象</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">window</span> <span class="p">=</span> <span class="n">NSScreen</span><span class="p">.</span><span class="n">main</span><span class="p">?.</span><span class="n">visibleFrame</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="设置大小">设置大小</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">HStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">frame</span><span class="p">(</span><span class="n">width</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">width</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">,</span> <span class="n">height</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">height</span> <span class="o">/</span> <span class="mf">1.5</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="汇总">汇总</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">Home</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">window</span> <span class="p">=</span> <span class="n">NSScreen</span><span class="p">.</span><span class="n">main</span><span class="p">?.</span><span class="n">visibleFrame</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">HStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Hello, World!&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">frame</span><span class="p">(</span><span class="n">width</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">width</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">,</span> <span class="n">height</span><span class="p">:</span> <span class="n">window</span><span class="p">!.</span><span class="n">height</span> <span class="o">/</span> <span class="mf">1.5</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目实现搜索功能 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%8A%9F%E8%83%BD/ + Mon, 07 Jun 2021 16:55:39 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%8A%9F%E8%83%BD/ + <h2 id="实现">实现</h2> +<h3 id="创建变量">创建变量</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">State</span> <span class="kd">var</span> <span class="nv">search</span><span class="p">:</span> <span class="nb">String</span> <span class="p">=</span> <span class="s">&#34;&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="过滤">过滤</h3> +<p>此处过滤条件为判断元素是否包含搜索的文本。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="n">Your</span><span class="o">-</span><span class="nb">Array</span><span class="p">&gt;.</span><span class="bp">filter</span><span class="p">({</span><span class="s">&#34;</span><span class="si">\(</span><span class="nv">$0</span><span class="si">)</span><span class="s">&#34;</span><span class="p">.</span><span class="bp">contains</span><span class="p">(</span><span class="n">search</span><span class="p">.</span><span class="n">lowercased</span><span class="p">())</span> <span class="o">||</span> <span class="n">search</span><span class="p">.</span><span class="bp">isEmpty</span><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="汇总">汇总</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">DataList</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">State</span> <span class="kd">var</span> <span class="nv">search</span><span class="p">:</span> <span class="nb">String</span> <span class="p">=</span> <span class="s">&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">Binding</span> <span class="kd">var</span> <span class="nv">dataList</span><span class="p">:</span> <span class="p">[</span><span class="n">Item</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">dataSearchFilterList</span><span class="p">:</span> <span class="p">[</span><span class="n">Item</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">dataList</span><span class="p">.</span><span class="bp">filter</span><span class="p">({</span><span class="s">&#34;</span><span class="si">\(</span><span class="nv">$0</span><span class="si">)</span><span class="s">&#34;</span><span class="p">.</span><span class="bp">contains</span><span class="p">(</span><span class="n">search</span><span class="p">.</span><span class="n">lowercased</span><span class="p">())</span> <span class="o">||</span> <span class="n">search</span><span class="p">.</span><span class="bp">isEmpty</span><span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">dataSearchFilterList</span><span class="p">.</span><span class="bp">isEmpty</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;搜索不到...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 展示搜索结果</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目Image点击事件 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AEimage%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6/ + Mon, 07 Jun 2021 16:43:17 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AEimage%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6/ + <h2 id="前言">前言</h2> +<p>swiftui中的点击可以有两种情况:</p> +<ul> +<li>Button</li> +<li>Gestures</li> +</ul> +<p>根据不同情况可以去不同地使用。</p> +<h2 id="单纯的按钮">单纯的按钮</h2> +<p>此处单纯的按钮即为有按钮样式和点击事件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Button</span><span class="p">(</span><span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 点击事件触发的代码</span> +</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Image</span><span class="p">(</span><span class="n">systemName</span><span class="p">:</span> <span class="s">&#34;plus&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="无样式的按钮">无样式的按钮</h2> +<p>即为没有按钮样式的按钮,方便直接展示<code>Image</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Button</span><span class="p">(</span><span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 点击事件触发的代码</span> +</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Image</span><span class="p">(</span><span class="n">systemName</span><span class="p">:</span> <span class="s">&#34;plus&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">buttonStyle</span><span class="p">(</span><span class="n">BorderlessButtonStyle</span><span class="p">())</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="tapgesture事件">TapGesture事件</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Image</span><span class="p">(</span><span class="n">systemName</span><span class="p">:</span> <span class="s">&#34;plus&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">onTapGesture</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> <span class="c1">// 点击事件触发的代码</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SwiftUI项目复制字符串到剪切板 + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%A4%8D%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + Mon, 07 Jun 2021 16:29:41 +0000 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%A4%8D%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + <h2 id="前言">前言</h2> +<p>这是个比较坑的问题,我一开始开发的是macos项目,到网上搜的方案基本都是使用<code>UIPasteboard</code>方法,但是偏偏用不了。</p> +<p>后来开发ios项目,用macos的就不行,发现<code>UIPasteboard</code>的可行,所以这里需要清楚的是,ios和macos的复制方法是不同的&hellip;&hellip;</p> +<h2 id="macos">MacOS</h2> +<h3 id="实现">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">copyToClipBoard</span><span class="p">(</span><span class="n">textToCopy</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">pasteBoard</span> <span class="p">=</span> <span class="n">NSPasteboard</span><span class="p">.</span><span class="n">general</span> +</span></span><span class="line"><span class="cl"> <span class="n">pasteBoard</span><span class="p">.</span><span class="n">clearContents</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">pasteBoard</span><span class="p">.</span><span class="n">setString</span><span class="p">(</span><span class="n">textToCopy</span><span class="p">,</span> <span class="n">forType</span><span class="p">:</span> <span class="p">.</span><span class="n">string</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="调用">调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">copyToClipBoard</span><span class="p">(</span><span class="n">textToCopy</span><span class="p">:</span> <span class="s">&#34;Hello,World!&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="ios">IOS</h2> +<h3 id="实现-1">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">UIPasteboard</span><span class="p">.</span><span class="n">general</span><span class="p">.</span><span class="n">setValue</span><span class="p">(&lt;</span><span class="n">Your</span><span class="o">-</span><span class="nb">String</span><span class="p">&gt;,</span> <span class="n">forPasteboardType</span><span class="p">:</span> <span class="n">kUTTypePlainText</span> <span class="k">as</span> <span class="nb">String</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="调用-1">调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">UIPasteboard</span><span class="p">.</span><span class="n">general</span><span class="p">.</span><span class="n">setValue</span><span class="p">(</span><span class="s">&#34;Hello,World!&#34;</span><span class="p">,</span> <span class="n">forPasteboardType</span><span class="p">:</span> <span class="n">kUTTypePlainText</span> <span class="k">as</span> <span class="nb">String</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Swift计算两个日期的天数差 + https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/ + Mon, 07 Jun 2021 15:12:29 +0000 + + https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/ + <h2 id="官方方法">官方方法</h2> +<h3 id="datecomponents"><strong>DateComponents</strong></h3> +<p>A date or time specified in terms of units (such as year, month, day, hour, and minute) to be evaluated in a calendar system and time zone.</p> +<p>以要在日历系统和时区中计算的单位(例如年、月、日、小时和分钟)指定的日期或时间。</p> +<h2 id="实现">实现</h2> +<h3 id="计算两个字符串形式的日期的天数差">计算两个字符串形式的日期的天数差</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">dateDiff</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 计算两个日期差,返回相差天数</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">calendar</span> <span class="p">=</span> <span class="n">Calendar</span><span class="p">.</span><span class="n">current</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateFormat</span> <span class="p">=</span> <span class="s">&#34;yyyy-MM-dd&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">today</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 开始日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">startDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-08&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 结束日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">endDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-09&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">diff</span><span class="p">:</span><span class="n">DateComponents</span> <span class="p">=</span> <span class="n">calendar</span><span class="p">.</span><span class="n">dateComponents</span><span class="p">([.</span><span class="n">day</span><span class="p">],</span> <span class="n">from</span><span class="p">:</span> <span class="n">startDate</span><span class="p">!,</span> <span class="n">to</span><span class="p">:</span> <span class="n">endDate</span><span class="p">!)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">diff</span><span class="p">.</span><span class="n">day</span><span class="p">!</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="计算当天跟某一天的天数差">计算当天跟某一天的天数差</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">checkDiff</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 计算两个日期差,返回相差天数</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">calendar</span> <span class="p">=</span> <span class="n">Calendar</span><span class="p">.</span><span class="n">current</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateFormat</span> <span class="p">=</span> <span class="s">&#34;yyyy-MM-dd&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当天</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">today</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">startDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="n">formatter</span><span class="p">.</span><span class="n">string</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="n">today</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 固定日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">endDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-09&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">diff</span><span class="p">:</span><span class="n">DateComponents</span> <span class="p">=</span> <span class="n">calendar</span><span class="p">.</span><span class="n">dateComponents</span><span class="p">([.</span><span class="n">day</span><span class="p">],</span> <span class="n">from</span><span class="p">:</span> <span class="n">startDate</span><span class="p">!,</span> <span class="n">to</span><span class="p">:</span> <span class="n">endDate</span><span class="p">!)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">diff</span><span class="p">.</span><span class="n">day</span><span class="p">!</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Swift UI项目调用core data + https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/ + Mon, 07 Jun 2021 11:24:22 +0000 + + https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/ + <h2 id="前言">前言</h2> +<p>这篇文章是我写的第一篇Swift UI相关的笔记吧。</p> +<p>这真的难搞哦,毕竟新出来的。国内文档真的是少之又少,国外的文档也真不多,基本搜出来的都是UIKit的。官方文档,也是一言难尽,基本就处于,我要实现一个功能,然后去搜一下各种帖子,筛选掉无用的帖子,找到有用的点,当然也是时常根本找不到有用的帖子,然后就要去油管上看各种教程,然后发现:哦!原来还有这个方法!接着去官方文档搜一下,看一下属性,自己调用调用&hellip;&hellip;</p> +<p>光是这个core data我就折腾了近一周,最后发现,原来还是官方好呀&hellip;..</p> +<p>吐槽一下自学swift ui,现在进入正题了。</p> +<p>core data呢,是苹果官方的本地数据库,但是其存储的文件其实是<code>sqlite</code>文件。其可以通过icloud实现备份和同步,当然icloud我会额外写一篇文档来详细讲述的(又是一把辛酸泪&hellip;)。</p> +<h2 id="环境">环境</h2> +<p>如下是我当前的环境:</p> +<ul> +<li>系统:macOS Big Sur 11.4</li> +<li>Xcode:Version 12.5 (12E262)</li> +<li>Swift:5.4</li> +</ul> +<h2 id="操作步骤">操作步骤</h2> +<h3 id="创建项目">创建项目</h3> +<p>在创建项目的时候,可以直接选择<code>Use Core Data</code>选项,xcode会直接在<code>ContentView.swift</code>中生成一个相关demo。</p> +<p><img src="https://blog.hunterji.com/../assets/create_swiftui_project.png" + + + + loading="lazy" + + alt="创建项目时候截图" + + +></p> +<h3 id="查看相关文件">查看相关文件</h3> +<p>创建之后,查看文件目录,相对于不选择<code>Use Core Data</code>,会多出如下几个文件:</p> +<ul> +<li><Your-Project-Name>.xcdatamodeId</li> +<li>Persistence.swift</li> +</ul> +<h3 id="创建core-data表">创建core data表</h3> +<p>点击<code>&lt;Your-Project-Name&gt;.xcdatamodeId</code>文件,进入页面。</p> +<p>可以看到页面内有默认的<code>CONFIGURATIONS</code>为<code>Default</code>,默认的<code>ENITITIES</code>为<code>Item</code>,可以理解为分别对应sql中的库和表。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_core_data_page.png" + + + + loading="lazy" + + alt="swiftui_core_data_page.png" + + +></p> +<p>然后点击<code>Item</code>,可以看到其字段,有默认的<code>timestamp</code>字段。</p> +<p>若要新增<code>ENTITIES</code>(表),点击底部的<code>Add Entity</code>按钮即可。</p> +<p>若要新增<code>Attributes</code>(字段),点击右侧<code>Attributes</code>中的<code>+</code>即可,注意字段要选择类型。</p> +<p>比如,此处以默认的<code>Item</code>为例,新增<code>username</code>和<code>age</code>两个字段。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_core_data_create_attributes.png" + + + + loading="lazy" + + alt="swiftui_core_data_create_attributes.png" + + +></p> +<h3 id="代码层面操作core-data">代码层面操作core data</h3> +<h4 id="1查看">1)查看</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">)</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">viewContext</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="n">FetchRequest</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">sortDescriptors</span><span class="p">:</span> <span class="p">[</span><span class="n">NSSortDescriptor</span><span class="p">(</span><span class="n">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="n">Item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">ascending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)],</span> +</span></span><span class="line"><span class="cl"> <span class="n">animation</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">var</span> <span class="nv">items</span><span class="p">:</span> <span class="n">FetchedResults</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// body中便利items即可</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="2新增">2)新增</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>如上是xcode自动生成的新增函数,若是要自定义添加字段可以这样改动下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// 此处按照如上添加Attributes修改,具体修改按照项目具体情况</span> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="nb">Int16</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">username</span> <span class="p">=</span> <span class="n">username</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="n">age</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3删除">3)删除</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="n">IndexSet</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">offsets</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">items</span><span class="p">[</span><span class="nv">$0</span><span class="p">]</span> <span class="p">}.</span><span class="n">forEach</span><span class="p">(</span><span class="n">viewContext</span><span class="p">.</span><span class="n">delete</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="4汇总">4)汇总</h4> +<p>在默认生成代码中,有一个<code>toolbar</code>,其在<code>macos</code>中可以生效,但是在<code>ios</code>中只有<code>EditionButton()</code>可以使用,为了方便演示,此处新增一个<code>Button</code>来添加数据。</p> +<p>其次有点要声明下,在xcode中写代码时,右侧的<code>canvas</code>会实时渲染,列表中出现的数据并不是<code>core data</code>中的数据,而是默认生成的<code>Persistence.swift</code>中生成的演示数据,只能看看,不能当真。只有在模拟器/实体机编译运行时才能操作<code>core data</code>。</p> +<p>如下为修改过后的<code>ContentView.swift</code>文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span><span class="lnt">71 +</span><span class="lnt">72 +</span><span class="lnt">73 +</span><span class="lnt">74 +</span><span class="lnt">75 +</span><span class="lnt">76 +</span><span class="lnt">77 +</span><span class="lnt">78 +</span><span class="lnt">79 +</span><span class="lnt">80 +</span><span class="lnt">81 +</span><span class="lnt">82 +</span><span class="lnt">83 +</span><span class="lnt">84 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"><span class="c1">// ContentView.swift</span> +</span></span><span class="line"><span class="cl"><span class="c1">// HelloKuari</span> +</span></span><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"><span class="c1">// Created by Kuari on 2021/6/5.</span> +</span></span><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">SwiftUI</span> +</span></span><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">CoreData</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">ContentView</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">)</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">viewContext</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">FetchRequest</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">sortDescriptors</span><span class="p">:</span> <span class="p">[</span><span class="n">NSSortDescriptor</span><span class="p">(</span><span class="n">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="n">Item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">ascending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)],</span> +</span></span><span class="line"><span class="cl"> <span class="n">animation</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">items</span><span class="p">:</span> <span class="n">FetchedResults</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">VStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">List</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Tom: </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">username</span><span class="p">!</span><span class="si">)</span><span class="s"> age: </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">age</span><span class="si">)</span><span class="s"> time : </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">!,</span> <span class="n">formatter</span><span class="p">:</span> <span class="n">itemFormatter</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">onDelete</span><span class="p">(</span><span class="n">perform</span><span class="p">:</span> <span class="n">deleteItems</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 新增一个按钮来添加数据</span> +</span></span><span class="line"><span class="cl"> <span class="n">Button</span><span class="p">(</span><span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="s">&#34;tom&#34;</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="mi">12</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Add Item&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="nb">Int16</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">username</span> <span class="p">=</span> <span class="n">username</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="n">age</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="n">IndexSet</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">offsets</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">items</span><span class="p">[</span><span class="nv">$0</span><span class="p">]</span> <span class="p">}.</span><span class="n">forEach</span><span class="p">(</span><span class="n">viewContext</span><span class="p">.</span><span class="n">delete</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">let</span> <span class="nv">itemFormatter</span><span class="p">:</span> <span class="n">DateFormatter</span> <span class="p">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateStyle</span> <span class="p">=</span> <span class="p">.</span><span class="n">short</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">timeStyle</span> <span class="p">=</span> <span class="p">.</span><span class="n">medium</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">formatter</span> +</span></span><span class="line"><span class="cl"><span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">ContentView_Previews</span><span class="p">:</span> <span class="n">PreviewProvider</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">static</span> <span class="kd">var</span> <span class="nv">previews</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ContentView</span><span class="p">().</span><span class="n">environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">,</span> <span class="n">PersistenceController</span><span class="p">.</span><span class="n">preview</span><span class="p">.</span><span class="n">container</span><span class="p">.</span><span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后点击左侧顶部的运行按钮,编译运行。</p> +<p>一开始是空白一片,点击<code>Add Item</code>按钮之后,便会开始添加数据。</p> +<img src="../assets/swiftui_core_data_add_item_demo.png" alt="swiftui_core_data_add_item_demo.png" style="zoom:50%;" /> +<p>对于记录右滑即可删除,其为<code>List</code>的<code>onDelete</code>方法。</p> +<h2 id="结语">结语</h2> +<p>该文章是面向新手的,也是记录下我踩过的坑,因为目前文档匮乏,身边也没swift的开发小伙伴儿,只能靠自己摸索,若有大佬有更好的方法,真的还请不吝赐教。</p> +<p>后面持续记录踩坑中&hellip;&hellip;</p> + + + + H5检测手机摇一摇 + https://blog.hunterji.com/p/h5%E6%A3%80%E6%B5%8B%E6%89%8B%E6%9C%BA%E6%91%87%E4%B8%80%E6%91%87/ + Wed, 28 Apr 2021 13:07:50 +0000 + + https://blog.hunterji.com/p/h5%E6%A3%80%E6%B5%8B%E6%89%8B%E6%9C%BA%E6%91%87%E4%B8%80%E6%91%87/ + <h2 id="简介">简介</h2> +<p>要实现h5检测手机摇一摇动作可以直接调用h5原生api。但是在我的实践中发现在<code>ios</code>中限制条件比较多,体验还是有些区别的。</p> +<h2 id="如何监听">如何监听</h2> +<p>调用<code>Window: devicemotion event</code>即可实现监听。<code>devicemotion</code>事件以固定的时间间隔触发,并指示设备当时在接收的加速物理力量。 它还提供有关旋转速率的信息(如果有)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleMotionEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">x</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">accelerationIncludingGravity</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">accelerationIncludingGravity</span><span class="p">.</span><span class="nx">y</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">z</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">accelerationIncludingGravity</span><span class="p">.</span><span class="nx">z</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// Do something awesome. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;devicemotion&#34;</span><span class="p">,</span> <span class="nx">handleMotionEvent</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="安卓机">安卓机</h2> +<p>安卓机上直接按照如上即可实现。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>测试摇一摇<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;phone&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;show&#34;</span><span class="p">&gt;</span>摇一摇<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleMotionEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;show&#39;</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;摇动中&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;devicemotion&#34;</span><span class="p">,</span> <span class="nx">handleMotionEvent</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s2">&#34;该浏览器不支持摇一摇功能&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="iphone">iPhone</h2> +<h3 id="限制">限制</h3> +<p>在<code>ios</code>上限制有两条:</p> +<ul> +<li>h5必须是<code>https</code>协议的</li> +<li>必须用户点击授权才可以调用<code>devicemotion</code></li> +</ul> +<h3 id="授权">授权</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getPermission</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span> <span class="o">!==</span> <span class="s1">&#39;undefined&#39;</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="s1">&#39;granted&#39;</span> <span class="o">===</span> <span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户同意授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户拒绝授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;error: &#39;</span> <span class="o">+</span> <span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>直接调用该函数请求授权会导致报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">error: NotAllowedError: Requesting device orientation or motion access requires a user gesture to prompt +</span></span></code></pre></td></tr></table> +</div> +</div><p>需要用户主动去请求授权,因此此处需要将调用放到比如一个按钮上,让用户去点击请求授权。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">&#34;getPermission()&#34;</span><span class="p">&gt;</span>请求授权<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="全部代码">全部代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>测试摇一摇<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;phone&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">&#34;getPermission()&#34;</span><span class="p">&gt;</span>请求授权<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;show&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleMotionEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;show&#39;</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;摇动中&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">startListen</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;devicemotion&#34;</span><span class="p">,</span> <span class="nx">handleMotionEvent</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s2">&#34;该浏览器不支持摇一摇功能&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getPermission</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span> <span class="o">!==</span> <span class="s1">&#39;undefined&#39;</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">DeviceMotionEvent</span><span class="p">.</span><span class="nx">requestPermission</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="s1">&#39;granted&#39;</span> <span class="o">===</span> <span class="nx">state</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户同意授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">startListen</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//用户拒绝授权 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;error: &#39;</span> <span class="o">+</span> <span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Grafana+Loki+Docker Driver Client日志收集方案 + https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + Wed, 14 Apr 2021 15:13:18 +0000 + + https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + <h2 id="简介">简介</h2> +<p>具体日志采集方案在<code>Grafana+Loki+Promtail日志收集方案</code>文章中已经介绍过,此处不再重复介绍。不太了解的小伙伴儿赶紧去复习!</p> +<p>此处主要是记录下<code>Docker Driver Client</code>方式的部署。</p> +<h2 id="docker-plugin">docker plugin</h2> +<h3 id="安装">安装</h3> +<p>安装<code>loki</code>插件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证">验证</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ docker plugin ls +</span></span><span class="line"><span class="cl">ID NAME DESCRIPTION ENABLED +</span></span><span class="line"><span class="cl">ac720b8fcfdb loki Loki Logging Driver <span class="nb">true</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="开启禁用">开启/禁用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin <span class="nb">enable</span> loki +</span></span><span class="line"><span class="cl">docker plugin disable loki --force +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="卸载">卸载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin rm loki +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="部署">部署</h2> +<h3 id="docker-composeyml">docker-compose.yml</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/loki:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3100:3100&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/loki/local-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">grafana</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:latest</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3000:3000&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行">运行</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker-compose up -d +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="容器运行形式">容器运行形式</h3> +<p>容器运行时需要修改<code>log-driver</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> --log-driver<span class="o">=</span>loki +</span></span><span class="line"><span class="cl">--log-opt loki-url<span class="o">=</span><span class="s2">&#34;http://&lt;loki-url&gt;/loki/api/v1/push&#34;</span> +</span></span><span class="line"><span class="cl">--log-opt loki-retries<span class="o">=</span><span class="m">5</span> +</span></span><span class="line"><span class="cl">--log-opt loki-batch-size<span class="o">=</span><span class="m">400</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>举个例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run --log-driver<span class="o">=</span>loki <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-url<span class="o">=</span><span class="s2">&#34;http://192.168.10.10:3100/loki/api/v1/push&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-retries<span class="o">=</span><span class="m">5</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-batch-size<span class="o">=</span><span class="m">400</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> -p 3000:3000 <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> nginx +</span></span></code></pre></td></tr></table> +</div> +</div><p>由此查看日志,其<code>labels</code>只有<code>container_name</code>。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://grafana.com/docs/loki/latest/clients/docker-driver/" target="_blank" rel="noopener" + >官方文档</a></li> +</ul> + + + + Grafana+Loki+Promtail日志收集方案 + https://blog.hunterji.com/p/grafana-loki-promtail%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + Wed, 14 Apr 2021 14:41:47 +0000 + + https://blog.hunterji.com/p/grafana-loki-promtail%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + <h2 id="前言">前言</h2> +<h3 id="简介">简介</h3> +<p><code>Grafana</code>项目由TorkelÖdegaard于2014年发起,最近几年成为GitHub上最受欢迎的开源项目之一。 它使您可以查询,可视化指标并记录警报,无论它们存储在何处。</p> +<p><code>Loki</code>是受Prometheus启发的水平可扩展,高度可用的多租户日志聚合系统。 它被设计为非常经济高效且易于操作。 它不索引日志的内容,而是为每个日志流设置一组标签。</p> +<p><code>Promtail</code>是将本地日志内容发送到私有Loki实例或Grafana Cloud的代理。 通常将其部署到需要监视应用程序的每台机器上。</p> +<h3 id="方案对比">方案对比</h3> +<p>为什么选择<code>Grafana</code> + <code>Loki</code> + <code>Promtail</code>的日志采集方案呢?</p> +<p>我尝试过如下几种方案:</p> +<ul> +<li><code>Elasticsearch</code>+<code>Kibana</code>+<code>Filebeat</code>: 运维成本低,侵入性低,但是对于高并发情况下效果不太好,消耗资源也稍高,需要考虑日志存储成本</li> +<li><code>Elasticsearch</code>+<code>Kibana</code>+<code>Logstash</code>+<code>Kafka</code>+<code>Filebeat</code>: 可以有效处理高并发情况,且在<code>elk</code>节点挂掉情况下不会丢失日志。但是,运维成本高,需要考虑日志存储成本,整套消耗资源比较高!</li> +<li><code>Grafana</code>+<code>Loki</code>+<code>Docker Driver Client</code>:使用Docker Driver的方式来直接获取容器的日志,配置较简单,但是需要物理机上安装docker plugin,和运行容器时设置<code>log-driver</code>,侵入性较高</li> +</ul> +<p>相比之下,当前选择的方案,对于我们当前业务场景下是较为合适的,轻量且侵入性低,由于是检测日志文件,无需担心存储成本。</p> +<h2 id="通用日志收集">通用日志收集</h2> +<p>首先介绍下通用日志收集版本的部署。只需要使用默认配置即可收集普通日志,可在<code>http://&lt;your-ip&gt;:3000</code>上查看日志详情。</p> +<h3 id="docker-compose">docker-compose</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/loki:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3100:3100&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/loki/local-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">promtail</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/promtail:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">/var/log:/var/log</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/promtail/config.yml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">grafana</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:latest</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3000:3000&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加数据源">添加数据源</h3> +<p>部署成功之后,打开<code>http://&lt;your-ip&gt;:3000</code>访问<code>Grafana</code>,在左侧菜单栏选择<code>Configuration</code>,默认进去<code>Data Sources</code>页面。</p> +<p>点击<code>Add data sources</code>按钮,选择<code>Loki</code>。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_2.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.02.57" + + +></p> +<p>填入<code>URL</code>即可,此处为<code>http://loki:3100</code>,具体要看实际部署。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_3.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.03.47" + + +></p> +<p>然后点击<code>Sace &amp; Test</code>添加。</p> +<h3 id="查看与筛选日志">查看与筛选日志</h3> +<p>在左侧菜单栏选择<code>Explore</code>进入页面,点击左上角的<code>Log brwser</code>按钮,可以查看该数据源的<code>labels</code>,如此处为日志文件。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_4.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.08.48" + + +></p> +<p>在页面顶部的输入框中输入官方的<code>LogQL</code>可以筛选日志。此处日志就不展示了,大家知道有就行了。</p> +<p><img src="https://blog.hunterji.com/../assets/loki_5.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午2.11.09" + + +></p> +<h2 id="docker容器日志收集">Docker容器日志收集</h2> +<p>此处详细介绍下关于docker容器日志收集。</p> +<h3 id="promtail配置文件">promtail配置文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c">##config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">server</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">http_listen_address</span><span class="p">:</span><span class="w"> </span><span class="m">0.0.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">http_listen_port</span><span class="p">:</span><span class="w"> </span><span class="m">9080</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">positions</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">filename</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/positions.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">clients</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">http://loki:3100/loki/api/v1/push</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">scrape_configs</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">job_name</span><span class="p">:</span><span class="w"> </span><span class="l">containers</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">static_configs</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">targets</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">localhost</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">job</span><span class="p">:</span><span class="w"> </span><span class="l">containerlogs</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">__path__</span><span class="p">:</span><span class="w"> </span><span class="l">/var/lib/docker/containers/*/*log</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">pipeline_stages</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">json</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">expressions</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">output</span><span class="p">:</span><span class="w"> </span><span class="l">log</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">stream</span><span class="p">:</span><span class="w"> </span><span class="l">stream</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">attrs</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">json</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">expressions</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">tag</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">attrs</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">regex</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">expression</span><span class="p">:</span><span class="w"> </span><span class="l">(?P&lt;container_name&gt;(?:[^|]*[^|]))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;tag&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">timestamp</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">format</span><span class="p">:</span><span class="w"> </span><span class="l">RFC3339Nano</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">time</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">labels</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c"># tag:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">stream</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">output</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">output</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="docker-compose-1">docker-compose</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/loki:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3100:3100&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/loki/local-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">promtail</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/promtail:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">/var/lib/docker/containers:/var/lib/docker/containers</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">./promtail-config.yaml:/mnt/config/promtail-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/mnt/config/promtail-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">grafana</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:latest</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3000:3000&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="容器日志展现形式">容器日志展现形式</h3> +<p>至此其实就已经可以在<code>Grafana</code>上看到当前容器的日志了,操作如上<code>通用日志采集</code>,但是其展现形式只是<code>filename</code>,也就是类似于<code>107728869f40afa5510879a0e372c77bb513d6154591193d375bfcd421357ed4.log</code>的以container_id展现的日志文件,难以辨认具体是哪个容器。(要是有功夫去登录服务器看下容器id,不如直接看下日志了&hellip;)</p> +<p>所以,为了能够使用容器名去查看日志,此处需要在容器启动时设置参数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">--log-driver json-file --log-opt <span class="nv">tag</span><span class="o">=</span><span class="s2">&#34;{{.Name}}&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>举个例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker run --name hello -p 8080:80 --log-driver json-file --log-opt <span class="nv">tag</span><span class="o">=</span><span class="s2">&#34;{{.Name}}&#34;</span> -d nginx +</span></span></code></pre></td></tr></table> +</div> +</div><p><img src="https://blog.hunterji.com/../assets/loki_1.png" + + + + loading="lazy" + + alt="截屏2021-04-14 下午1.55.57" + + +></p> +<p>对于使用container name,还有<a class="link" href="https://github.com/grafana/loki/issues/333" target="_blank" rel="noopener" + >另一种方案</a>,就是每次生成container_name和container_id的映射表,个人认为比较麻烦,有兴趣的小伙伴儿可以尝试下。</p> +<h3 id="参考文档">参考文档</h3> +<ul> +<li><a class="link" href="https://grafana.com/docs/loki/latest/" target="_blank" rel="noopener" + >Loki官方文档</a></li> +<li><a class="link" href="https://gist.github.com/ruanbekker/c6fa9bc6882e6f324b4319c5e3622460" target="_blank" rel="noopener" + >ruanbekker/promtail_docker_logs</a></li> +<li><a class="link" href="https://github.com/grafana/loki/issues/333" target="_blank" rel="noopener" + >Display docker logs with human/logical name</a></li> +</ul> + + + + Promise inside request interceptor + https://blog.hunterji.com/p/promise-inside-request-interceptor/ + Fri, 09 Apr 2021 11:06:25 +0000 + + https://blog.hunterji.com/p/promise-inside-request-interceptor/ + <h2 id="问题">问题</h2> +<p>在使用axios的拦截器时候,需要在request中调用一个promise函数,因此需要等待其执行完成才能去进行下一步。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getToken</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(...)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Request interceptors +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">service</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">config</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">getToken</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getToken</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(...)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Request interceptors +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">service</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="kr">async</span> <span class="nx">config</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">awit</span> <span class="nx">getToken</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + tailwindcss基础 + https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/ + Fri, 05 Feb 2021 11:16:28 +0000 + + https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/ + <h2 id="简介">简介</h2> +<blockquote> +<p>Rapidly build modern websites without ever leaving your HTML.</p> +</blockquote> +<p><a class="link" href="https://tailwindcss.com/" target="_blank" rel="noopener" + >Tailwind CSS</a>可以快速建立现代网站,而无需离开HTML。其特性是原子化,很像的<code>BootStrap</code>的css。</p> +<p>通俗点解释就是,其封装了很多独立的css样式,只需要在html中添加<code>class</code>即可调用,而不需要去从头写css样式。</p> +<h2 id="安装">安装</h2> +<h3 id="下载包">下载包</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install tailwindcss@latest postcss@latest autoprefixer@latest +</span></span></code></pre></td></tr></table> +</div> +</div><p>可能会遇到如下报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Error: PostCSS plugin tailwindcss requires PostCSS 8. +</span></span></code></pre></td></tr></table> +</div> +</div><p>那就需要降低<code>PostCSS</code>的版本。如下,先卸载,再去安装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm uninstall tailwindcss postcss autoprefixer +</span></span><span class="line"><span class="cl">npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加tailwind作为postcss插件">添加Tailwind作为PostCSS插件</h3> +<p>添加<code>tailwindcss</code>和<code>autoprefixer</code>到<code>PostCSS</code>配置。大部分情况下作为<code>postcss.config.js</code>文件放在项目的顶级路径下。其也能作为<code>.postcssrc</code>文件,或者使用<code>postcss</code>键放在<code>package.json</code>文件中。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// postcss.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">tailwindcss</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">autoprefixer</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建配置文件">创建配置文件</h3> +<p>如果想自定义安装,当使用<code>npm</code>安装<code>tailwindcss</code>时候需要使用tailwind命令行去生成一个配置文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npx tailwindcss init +</span></span></code></pre></td></tr></table> +</div> +</div><p>这将会创建一个最小化的<code>tailwind.config.js</code>文件,其位于项目的顶级路径下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// tailwind.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">purge</span><span class="o">:</span> <span class="p">[],</span> +</span></span><span class="line"><span class="cl"> <span class="nx">darkMode</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// or &#39;media&#39; or &#39;class&#39; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">theme</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">variants</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在css中包含tailwind">在CSS中包含Tailwind</h3> +<p>创建<code>styles.css</code>文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* ./your-css-folder/styles.css */</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">base</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">components</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">utilities</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>引入该文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./styles.css&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="构建css">构建CSS</h3> +<p>为生产而构建时,确保配置清除选项以删除任何最小文件大小的未使用类。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// tailwind.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">purge</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;./index.html&#34;</span><span class="p">,</span> <span class="s2">&#34;./src/**/*.{vue,js,ts,jsx,tsx}&#34;</span><span class="p">],</span> <span class="c1">// 修改此行 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">darkMode</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// or &#39;media&#39; or &#39;class&#39; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">theme</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">variants</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[]</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="简要说明">简要说明</h2> +<p>由于其样式属性巨多,此处只举几例作简要说明,讲解基础用法。在开始不熟悉的情况下,要开着其手册查询。</p> +<h3 id="width">Width</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>w-0</td> +<td>width: 0px;</td> +</tr> +<tr> +<td>w-1</td> +<td>width: 0.25rem;</td> +</tr> +<tr> +<td>w-1/2</td> +<td>width: 50%;</td> +</tr> +<tr> +<td>w-full</td> +<td>width: 100%;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;w-1/2&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="padding">Padding</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>p-0</td> +<td>padding: 0px;</td> +</tr> +<tr> +<td>p-5</td> +<td>padding: 1.25rem;</td> +</tr> +<tr> +<td>pl-1</td> +<td>padding-left: 0.25rem;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;p-5&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="position">Position</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>static</td> +<td>position: static;</td> +</tr> +<tr> +<td>fixed</td> +<td>position: fixed;</td> +</tr> +<tr> +<td>absolute</td> +<td>position: absolute;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;static&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Static parent<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;absolute bottom-0 left-0 ...&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Absolute child<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="flex垂直居中">Flex垂直居中</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-row justify-center items-center&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>1<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>2<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="简单案例">简单案例</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col justify-center items-center p-20&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in 10&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-row justify-between items-center w-1/5 bg-gray-100 m-5 p-10 cursor-pointer shadow rounded hover:shadow-lg transition duration-300 ease-in-out&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;@/assets/message.png&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;logo&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;50px&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;50px&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col ml-5&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-lg&#34;</span><span class="p">&gt;</span>今天晚上加{{ item }}个鸡腿<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-sm text-gray-500&#34;</span><span class="p">&gt;</span>2020.2.{{ item }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + sqlx建模、连接与使用 + https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/ + Fri, 25 Dec 2020 12:51:12 +0000 + + https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/ + <h2 id="前言">前言</h2> +<p>为什么要用<code>sqlx</code>而不是<code>gorm</code>呢?是因为<code>orm</code>学习成本比较高,当使用<code>python</code>时候需要使用<code>sqlalchemy</code>,遇到<code>go</code>就要换成<code>gorm</code>,换成别的语言就又有其他orm。而直接使用原生<code>sql</code>可以减少学习成本,适用于所有开发语言。其次,<code>gorm</code>本身支持软删除,但是其对软删除的支持上存在缺陷,在单条查询可以过滤软删除数据,但是在多条查询时无法有效过滤,就造成了有时候要手动过滤又有时候不要手动过滤,使用体验非常差。</p> +<p>因此此处考虑去使用<code>sqlx</code>来直接调用原生<code>sql</code>。</p> +<h2 id="安装">安装</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">go</span> <span class="nx">get</span> <span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">jmoiron</span><span class="o">/</span><span class="nx">sqlx</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="连接数据库">连接数据库</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">database</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="s">&#34;github.com/go-sql-driver/mysql&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/jmoiron/sqlx&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">DBConnect</span><span class="p">()</span> <span class="o">*</span><span class="nx">sqlx</span><span class="p">.</span><span class="nx">DB</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">env</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;NODE_ENV&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">password</span><span class="p">,</span> <span class="nx">dbname</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 使用环境变量来切换生产和开发环境 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">env</span> <span class="o">==</span> <span class="s">&#34;production&#34;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">host</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbHost&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbUser&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbPassword&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dbname</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbname&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">host</span> <span class="p">=</span> <span class="s">&#34;&lt;your-host&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="s">&#34;&lt;your-port&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="p">=</span> <span class="s">&#34;&lt;your-user&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="s">&#34;&lt;your-password&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dbname</span> <span class="p">=</span> <span class="s">&#34;&lt;your-db-name&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">dbConfig</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s:%s@tcp(%s:%s)/%s?parseTime=true&#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">password</span><span class="p">,</span> <span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">dbname</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">db</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlx</span><span class="p">.</span><span class="nf">Connect</span><span class="p">(</span><span class="s">&#34;mysql&#34;</span><span class="p">,</span> <span class="nx">dbConfig</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;failed to connect database&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">db</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="创建表和调用">创建表和调用</h2> +<h3 id="创建表">创建表</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">test_gin</span><span class="p">.</span><span class="n">todo</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">(</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">todo_id</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="n">auto_increment</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;todo标题&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;内容&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">user_id</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;用户id&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="k">timestamp</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;创建时间戳&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">updated_at</span><span class="w"> </span><span class="k">timestamp</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">update</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;更新时间戳&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">is_deleted</span><span class="w"> </span><span class="n">tinyint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;是否被删除,0:未删,1:已删&#39;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">);</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建struct">创建struct</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Todo</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TodoID</span> <span class="kt">int</span> <span class="s">`db:&#34;todo_id&#34; json:&#34;todo_id,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`db:&#34;title&#34; json:&#34;title,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`db:&#34;content&#34; json:&#34;content,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UserID</span> <span class="kt">int</span> <span class="s">`db:&#34;user_id&#34; json:&#34;user_id,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">CreatedAt</span> <span class="kt">string</span> <span class="s">`db:&#34;created_at&#34; json:&#34;created_at,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UpdatedAt</span> <span class="kt">string</span> <span class="s">`db:&#34;updated_at&#34; json:&#34;updated_at,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">IsDeleted</span> <span class="kt">bool</span> <span class="s">`db:&#34;is_deleted&#34; json:&#34;is_deleted,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="封装方法">封装方法</h3> +<p>此处以增删为例。封装常用方法是为了复用,封装时候使用害羞的代码。不需要为了封装而封装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 新增Todo +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">Todo</span><span class="p">)</span> <span class="nf">Add</span><span class="p">()</span> <span class="p">(</span><span class="nx">todoID</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 连接数据库 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 执行添加sql +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;insert into todo (title, content, user_id) value (?, ?, ?)&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">UserID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lastTodoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">result</span><span class="p">.</span><span class="nf">LastInsertId</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">todoID</span> <span class="p">=</span> <span class="nb">int</span><span class="p">(</span><span class="nx">lastTodoID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 删除Todo +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">Todo</span><span class="p">)</span> <span class="nf">Del</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;update todo set is_deleted = 0 where is_deleted = 0 and todo_id = ?&#34;</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">TodoID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="视图中使用">视图中使用</h2> +<p>此处以新增接口为例。</p> +<h3 id="调用封装的方法">调用封装的方法</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">views</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testGin/models&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// addTodo.go -- post +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AddTodo</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// {&#34;title&#34;: &#34;hello&#34;, &#34;content&#34;: &#34;world&#34;} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">requestBody</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`json:&#34;content&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBind</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">requestBody</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数有误&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">todo</span> <span class="o">:=</span> <span class="nx">models</span><span class="p">.</span><span class="nx">Todo</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span><span class="p">:</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span><span class="p">:</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UserID</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="c1">// 此处的1为假数据,此处应当从上下文获取请求用户的user_id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">todoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">todo</span><span class="p">.</span><span class="nf">Add</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;todo_id&#34;</span><span class="p">:</span> <span class="nx">todoID</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="直接使用">直接使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">views</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testGin/database&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// addTodo.go -- post +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AddTodo</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// {&#34;title&#34;: &#34;hello&#34;, &#34;content&#34;: &#34;world&#34;} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">requestBody</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`json:&#34;content&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBind</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">requestBody</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数有误&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 连接数据库 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 执行添加sql +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;insert into todo (title, content, user_id) value (?, ?, ?)&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lastTodoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">result</span><span class="p">.</span><span class="nf">LastInsertId</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;todo_id&#34;</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="nx">lastTodoID</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + gin中间件和鉴权 + https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/ + Fri, 18 Dec 2020 14:02:30 +0000 + + https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/ + <h2 id="前言">前言</h2> +<p><code>gin</code>的中间件的使用场景非常广泛,此处主要介绍如何使用其来完成常见场景下的鉴权。</p> +<h2 id="官方文档">官方文档</h2> +<p>官方文档列出了如下几种使用方式:</p> +<ul> +<li><a class="link" href="https://github.com/gin-gonic/gin#using-middleware" target="_blank" rel="noopener" + >使用中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#custom-middleware" target="_blank" rel="noopener" + >定制中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#using-basicauth-middleware" target="_blank" rel="noopener" + >使用基础认证的中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#goroutines-inside-a-middleware" target="_blank" rel="noopener" + >中间件使用协程</a></li> +</ul> +<h2 id="不同场景的鉴权实现">不同场景的鉴权实现</h2> +<h3 id="api-key">api key</h3> +<p>对于<code>api key</code>的方式需要设置白名单,对白名单外的请求进行<code>token</code>检测。此中间件在处理请求被处理之前对请求进行拦截,验证token,因此可在此处利用<code>gin.Context</code>来设置上下文,如请求所属用户的用户信息等。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">middleware</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/url&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;strings&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">whiteList</span><span class="p">()</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;/ping&#34;</span><span class="p">:</span> <span class="s">&#34;GET&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">withinWhiteList</span><span class="p">(</span><span class="nx">url</span> <span class="o">*</span><span class="nx">url</span><span class="p">.</span><span class="nx">URL</span><span class="p">,</span> <span class="nx">method</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">target</span> <span class="o">:=</span> <span class="nf">whiteList</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">queryUrl</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">url</span><span class="p">),</span> <span class="s">&#34;?&#34;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">target</span><span class="p">[</span><span class="nx">queryUrl</span><span class="p">];</span> <span class="nx">ok</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">target</span><span class="p">[</span><span class="nx">queryUrl</span><span class="p">]</span> <span class="o">==</span> <span class="nx">method</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Authorize</span><span class="p">()</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">HandlerFunc</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">type</span> <span class="nx">QueryToken</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Token</span> <span class="kt">string</span> <span class="s">`binding:&#34;required,len=3&#34; form:&#34;token&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当路由不在白名单内时进行token检测 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">!</span><span class="nf">withinWhiteList</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">URL</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">Method</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">queryToken</span> <span class="nx">QueryToken</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindQuery</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">queryToken</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">AbortWithStatusJSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">,</span> <span class="s">&#34;user&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="路由权限">路由权限</h3> +<h4 id="1说明">1)说明</h4> +<p>对于请求的处理,需要去验证是否对其请求的路径拥有访问权限。</p> +<p>首先看一下<code>gin</code>的路由设置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">group</span> <span class="o">*</span><span class="nx">RouterGroup</span><span class="p">)</span> <span class="nf">POST</span><span class="p">(</span><span class="nx">relativePath</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">handlers</span> <span class="o">...</span><span class="nx">HandlerFunc</span><span class="p">)</span> <span class="nx">IRoutes</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>其参数为<code>...HandlerFunc</code>,其解释为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">HandlerFunc</span> <span class="kd">func</span><span class="p">(</span><span class="o">*</span><span class="nx">Context</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">HandlerFunc</span> <span class="nx">defines</span> <span class="nx">the</span> <span class="nx">handler</span> <span class="nx">used</span> <span class="nx">by</span> <span class="nx">gin</span> <span class="nx">middleware</span> <span class="nx">as</span> <span class="k">return</span> <span class="nx">value</span><span class="p">.</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>所以此处可以通过定制中间件的方式实现一个路由权限处理。</p> +<p>当然此处的权限处理比较简单,使用角色直接去判断权限。如分为两个角色,管理员<code>admin</code>和普通用户<code>user</code>。</p> +<p>不过此处实现有个前提条件,就是如何拿到用户的角色呢?此处需要在上一步(<code>api key</code>)的实现中加上利用<code>gin.Context</code>设置角色:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">c</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">,</span> <span class="s">&#34;admin&#34;</span><span class="p">)</span> <span class="c1">// 可见上一步的代码,当然此处只是为了演示设置固定值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后在中间件中拿到角色并进行判断。</p> +<h4 id="2路由权限中间件">2)路由权限中间件</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">middleware</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;errors&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Permissions</span><span class="p">(</span><span class="nx">roles</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">HandlerFunc</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">permissionsErr</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 获取上下文中的用户角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">roleValue</span><span class="p">,</span> <span class="nx">exists</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">exists</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;获取用户信息失败&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">role</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">roleValue</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断请求的用户的角色是否属于设定角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">noAccess</span> <span class="o">:=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="nx">roles</span><span class="p">);</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">role</span> <span class="o">==</span> <span class="nx">roles</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">noAccess</span> <span class="p">=</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">noAccess</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;权限不够&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">permissionsErr</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">AbortWithStatusJSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3使用">3)使用</h4> +<p>在设置路由时候,添加该中间件,并设置白名单。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">r</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="nx">middleware</span><span class="p">.</span><span class="nf">Permissions</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;admin&#34;</span><span class="p">}),</span> <span class="nx">views</span><span class="p">.</span><span class="nx">AddTodo</span><span class="p">)</span> <span class="c1">// 添加中间件将会验证角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">r</span><span class="p">.</span><span class="nf">PUT</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="nx">views</span><span class="p">.</span><span class="nx">ModifyTodo</span><span class="p">)</span> <span class="c1">// 未添加中间件则不会验证角色 +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + go-Redis的发布与订阅 + https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/ + Fri, 27 Nov 2020 17:41:53 +0000 + + https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/ + <h2 id="前言">前言</h2> +<p>在数据量较小的情况下,可以使用<code>Redis</code>来实现消息的发布与订阅,来代替<code>Kafka</code>。<code>Kafka</code>对于数据量大的场景下性能卓越,但是对于如此小场景时候,不仅运维成本提升,还用不上多少性能。</p> +<p>不过使用<code>Redis</code>的另一个弊端是消息不能堆积,一旦消费者节点没有消费消息,消息将会丢失。因此需要评估当下场景来选择适合的架构。</p> +<p>此处使用go-redis来实现<code>Redis</code>的发布与订阅。</p> +<h2 id="官方文档">官方文档</h2> +<p><a class="link" href="https://pkg.go.dev/github.com/go-redis/redis/v8#PubSub" target="_blank" rel="noopener" + >官方文档</a>有较为完整的例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;mychannel1&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Wait for confirmation that subscription is created before publishing anything. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Go channel which receives messages. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Publish a message. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;mychannel1&#34;</span><span class="p">,</span> <span class="s">&#34;hello&#34;</span><span class="p">).</span><span class="nf">Err</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">time</span><span class="p">.</span><span class="nf">AfterFunc</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// When pubsub is closed channel is closed too. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Consume messages. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="代码实现">代码实现</h2> +<p>分步讲解下具体实现代码。</p> +<h3 id="连接redis">连接redis</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="发布消息">发布消息</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="订阅消息">订阅消息</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整案例">完整案例</h2> +<p>此处分为一个发布节点和一个订阅节点来实现了简单的发布与订阅。</p> +<h3 id="消息发布节点">消息发布节点</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">channel</span> <span class="o">:=</span> <span class="s">&#34;hello&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">msgList</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;hello&#34;</span><span class="p">,</span> <span class="s">&#34;world&#34;</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 此处发了两个消息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">msgList</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;已经发送%s到%s\n&#34;</span><span class="p">,</span> <span class="nx">msg</span><span class="p">,</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="消息订阅节点">消息订阅节点</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">channel</span> <span class="o">:=</span> <span class="s">&#34;hello&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行结果">运行结果</h2> +<h3 id="消息发布节点输出">消息发布节点输出</h3> +<img src="../assets/go_redis_pub.png" style="zoom:50%;" /> +<h3 id="消息订阅节点输出">消息订阅节点输出</h3> +<img src="../assets/go_redis_sub.png" style="zoom:50%;" /> + + + go单元测试 + https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + Fri, 20 Nov 2020 15:07:06 +0000 + + https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + <h2 id="前言">前言</h2> +<p>想要写出好的 Go 程序,单元测试是很重要的一部分。 <code>testing</code> 包为提供了编写单元测试所需的工具,写好单元测试后,可以通过 <code>go test</code> 命令运行测试。</p> +<h2 id="规则">规则</h2> +<p><code>testing</code> 为 Go 语言 package 提供自动化测试的支持。通过 <code>go test</code> 命令,能够自动执行如下形式的任何函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestXxx</span><span class="p">(</span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 <code>TestXxx</code> 函数,如上所述。 将该文件放在与被测试文件相同的包中。该文件将被排除在正常的程序包之外,但在运行 <code>go test</code> 命令时将被包含。</p> +<h2 id="代码结构">代码结构</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── go.mod +</span></span><span class="line"><span class="cl">├── intMinBasicDriven_test.go +</span></span><span class="line"><span class="cl">├── intMinBasic_test.go +</span></span><span class="line"><span class="cl">└── main.go +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="第一个单元测试">第一个单元测试</h2> +<h3 id="要被测试的代码">要被测试的代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// main.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IntMin</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 返回a与b中的较小值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">a</span> <span class="p">&lt;</span> <span class="nx">b</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">a</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">b</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="测试代码">测试代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// intMinBasic_test.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestIntMinBasic</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ans</span> <span class="o">:=</span> <span class="nf">IntMin</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ans</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// t.Error* 会报告测试失败的信息,然后继续运行测试。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// t.Fail* 会报告测试失败的信息,然后立即终止测试。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;IntMin(2, -2) = %d; want -2&#34;</span><span class="p">,</span> <span class="nx">ans</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行测试">运行测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go <span class="nb">test</span> +</span></span><span class="line"><span class="cl">// 输出 +</span></span><span class="line"><span class="cl">ok heihei 0.385s +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="table-driven-test">Table-Driven Test</h2> +<p>单元测试可以重复,所以会经常使用 <em>表驱动</em> 风格编写单元测试, 表中列出了输入数据,预期输出,使用循环,遍历并执行测试逻辑。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// intMinBasicDriven_test.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestIntMinTableDriven</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">tests</span> <span class="p">=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">want</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="p">}{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// t.Run 可以运行一个 “subtests” 子测试,一个子测试对应表中一行数据。 运行 go test -v 时,他们会分开显示。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tt</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tests</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">testname</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%d,%d&#34;</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">a</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">testname</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ans</span> <span class="o">:=</span> <span class="nf">IntMin</span><span class="p">(</span><span class="nx">tt</span><span class="p">.</span><span class="nx">a</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ans</span> <span class="o">!=</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;got %d, want %d&#34;</span><span class="p">,</span> <span class="nx">ans</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行代码">运行代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go <span class="nb">test</span> -v +</span></span><span class="line"><span class="cl">// <span class="nv">输出</span> +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN <span class="nv">TestIntMinTableDriven</span> +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/0,1 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/1,0 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/2,-2 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/0,-1 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/-1,0 +</span></span><span class="line"><span class="cl">--- PASS: TestIntMinTableDriven <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/0,1 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/1,0 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/2,-2 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/0,-1 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/-1,0 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl">PASS +</span></span><span class="line"><span class="cl">ok heihei 0.566s +</span></span></code></pre></td></tr></table> +</div> +</div> + + + gin的http单元测试 + https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + Fri, 20 Nov 2020 14:47:04 +0000 + + https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + <h2 id="前言">前言</h2> +<p>Go 标准库专门提供了 <code>net/http/httptest</code> 包专门用于进行 http Web 开发测试。</p> +<p>此处基于gin来实现http的单元测试。</p> +<h2 id="get请求">GET请求</h2> +<p>此处使用http单元测试对<code>/ping</code>请求测试,其正常会返回字符串<code>pong</code>,响应码为<code>200</code>。</p> +<h3 id="web应用">web应用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setupRouter</span><span class="p">()</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Engine</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/ping&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="s">&#34;pong&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="http单元测试">http单元测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http/httptest&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/stretchr/testify/assert&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestPingRoute</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建http server并发起请求 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">,</span> <span class="s">&#34;/ping&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> <span class="c1">// 断言响应码为200 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;pong&#34;</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span> <span class="c1">// 断言响应为&#34;pong&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="postputdelete请求">POST/PUT/DELETE请求</h2> +<p>post、put、delete请求处理相似,都是处理其request body,因此此处只以post为例。</p> +<p>此处对<code>/todo</code>进行测试,其正常返回为<code>json</code>,其中<code>code</code>为<code>20000</code>,响应码为<code>200</code>。</p> +<h3 id="web应用-1">web应用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setupRouter</span><span class="p">()</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Engine</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">type</span> <span class="nx">ToDo</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TodoId</span> <span class="kt">int</span> <span class="s">`binding:&#34;required&#34; json:&#34;todo_id&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">todo</span> <span class="nx">ToDo</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindJSON</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">todo</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数不全&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 处理代码,此处以打印为例,省略处理... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">todo</span><span class="p">.</span><span class="nx">TodoId</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="http单元测试-1">http单元测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestTodoCreate</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 请求方法 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">method</span> <span class="o">:=</span> <span class="s">&#34;POST&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 请求路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">urlStr</span> <span class="o">:=</span> <span class="s">&#34;/todo&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// request body +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">body</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;title&#34;</span><span class="p">:</span> <span class="s">&#34;hello&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonByte</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">urlStr</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">jsonByte</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nx">routers</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> <span class="c1">// 判断响应码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">response</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">()),</span> <span class="o">&amp;</span><span class="nx">response</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">response</span><span class="p">[</span><span class="s">&#34;code&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">20000</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="c1">// 判断自定义状态码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="封装测试请求">封装测试请求</h2> +<p>由于http单元测试代码中存在较多重复,因此此处封装重复代码。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">tests</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">TestConfig</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Url</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Method</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Body</span> <span class="kd">interface</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">tc</span> <span class="o">*</span><span class="nx">TestConfig</span><span class="p">)</span> <span class="nf">Request</span><span class="p">()</span> <span class="o">*</span><span class="nx">httptest</span><span class="p">.</span><span class="nx">ResponseRecorder</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Body</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonByte</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="p">=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Url</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">jsonByte</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="p">=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Url</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nx">routers</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">w</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="封装后的单元测试">封装后的单元测试</h2> +<p>此处以post请求为例。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAddTag</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 新增tag +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">addTestConfig</span> <span class="nx">tests</span><span class="p">.</span><span class="nx">TestConfig</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Method</span> <span class="p">=</span> <span class="s">&#34;POST&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Url</span> <span class="p">=</span> <span class="s">&#34;/tag&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Body</span> <span class="p">=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;tag_name&#34;</span><span class="p">:</span> <span class="s">&#34;HelloHello&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nf">Request</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">addResponse</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">()),</span> <span class="o">&amp;</span><span class="nx">addResponse</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">addResponse</span><span class="p">[</span><span class="s">&#34;code&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">20000</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">lastInsertTagId</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">addResponse</span><span class="p">[</span><span class="s">&#34;tag_id&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask设置全局错误捕获 + https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/ + Mon, 02 Nov 2020 20:51:02 +0000 + + https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/ + <h2 id="前言">前言</h2> +<p>代码运行过程中,意外情况会导致<code>500</code>错误,对于使用者来说体验很不好,对于开发者来说也无法及时获取错误,需要去查看日志。</p> +<p>并且有的插件在某些报错情况下会返回一些敏感信息,非常危险。因此需要去捕获全局错误,通知开发者,自定义错误消息等。</p> +<h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">app</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">werkzeug.exceptions</span> <span class="kn">import</span> <span class="n">HTTPException</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="ne">Exception</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">all_exception_handler</span><span class="p">(</span><span class="n">e</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span> <span class="o">==</span> <span class="mi">404</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">40004</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;message&#39;</span><span class="p">:</span> <span class="s1">&#39;404&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">404</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1"># 通知开发者/写入日志</span> +</span></span><span class="line"><span class="cl"> <span class="n">handle</span><span class="p">(</span><span class="n">path</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">content</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;message&#39;</span><span class="p">:</span> <span class="s1">&#39;Error&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + flask_restful限制request字段长度 + https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/ + Mon, 02 Nov 2020 20:24:35 +0000 + + https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/ + <h2 id="前言">前言</h2> +<p>当前产品遇到一个报错,就是接口收到请求没有限制请求字段长度,导致字段长度超过数据库对应字段长度,直接报了500。因此也对此有些新的需求,需要在后端限制请求字段最大长度。</p> +<h2 id="环境">环境</h2> +<ul> +<li><strong>开发语言</strong>:Python 3.7</li> +<li><strong>后端框架</strong>:Flask 1.1.1</li> +<li><strong>插件</strong>:Flask-RESTful 0.3.8</li> +</ul> +<h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">field_max_limit</span><span class="p">(</span><span class="n">max_length</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field must be String.&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_length</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field cannot exceed </span><span class="si">%i</span><span class="s2"> characters.&#34;</span> <span class="o">%</span> <span class="n">max_length</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 解析请求参数时候验证长度</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="示例">示例</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_restful</span> <span class="kn">import</span> <span class="n">Api</span><span class="p">,</span> <span class="n">Resource</span><span class="p">,</span> <span class="n">reqparse</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">werkzeug.routing</span> <span class="kn">import</span> <span class="n">ValidationError</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">api</span> <span class="o">=</span> <span class="n">Api</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">field_max_limit</span><span class="p">(</span><span class="n">max_length</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field must be String.&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_length</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field cannot exceed </span><span class="si">%i</span><span class="s2"> characters.&#34;</span> <span class="o">%</span> <span class="n">max_length</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Login</span><span class="p">(</span><span class="n">Resource</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;username&#39;</span><span class="p">:</span> <span class="n">args</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;password&#39;</span><span class="p">:</span> <span class="n">args</span><span class="o">.</span><span class="n">password</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">20000</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">api</span><span class="o">.</span><span class="n">add_resource</span><span class="p">(</span><span class="n">Login</span><span class="p">,</span> <span class="s1">&#39;/login&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Docker修改时区 + https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/ + Wed, 14 Oct 2020 23:21:39 +0000 + + https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/ + <h2 id="前言">前言</h2> +<p>在使用<code>Docker</code>时,其默认时区并非使用者所在时区,需要进行修改。对于单个容器,当前修改有几种常见方式,比如直接映射宿主机时区到容器内,而本文介绍的为使用<code>Dockerfile</code>来直接修改镜像时区。此处仅以常见几个基础容器为例来介绍。</p> +<h2 id="常见容器">常见容器</h2> +<h3 id="alpine"><strong>Alpine</strong></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> alpine:latest</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装tzdata</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apk add --no-cache tzdata<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置时区</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">TZ</span><span class="o">=</span><span class="s2">&#34;Asia/Shanghai&#34;</span><span class="err"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t alpine:time . +</span></span><span class="line"><span class="cl">docker run --rm -it alpine:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="ubuntu"><strong>Ubuntu</strong></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> ubuntu</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置localtime</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 此处需要优先设置localtime,否则安装tzdata将会进入时区选择</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装tzdata</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apt-get update <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> <span class="o">&amp;&amp;</span> apt-get install tzdata -y <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> <span class="o">&amp;&amp;</span> apt-get clean<span class="err"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t ubuntu:time . +</span></span><span class="line"><span class="cl">docker run --rm -it ubuntu:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="debian">Debian</h3> +<ul> +<li>Debian中已经安装了<code>tzdata</code>,所以跟<code>Ubuntu</code>有所不通过</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> debian</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 修改设置dpkg为自动配置</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>noninteractive +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">RUN</span> ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> dpkg-reconfigure -f noninteractive tzdata<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 修改设置dpkg为手动输入选择操作</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>dialog +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t debian:time . +</span></span><span class="line"><span class="cl">docker run --rm -it debian:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>此处不再列举太多,主要解决方式为安装<code>tzdata</code>,然后修改时区。</p> + + + + 基于python3和js的前后端aes加解密 + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + Fri, 09 Oct 2020 21:12:50 +0000 + + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + <h2 id="简述">简述</h2> +<p>在特定敏感数据的场景需要加密,一开始采用<code>rsa</code>加密,但是<code>rsa</code>加密对性能要求较高,在解密时候对于数据量限制较大,导致加密传输的数据量上限较低。而采用<code>Base64</code>虽然简单明了但是解密过于简单。因此采用折中的对称加密<code>aes</code>。</p> +<p>而<code>aes</code>加密需要前后端加密类型相同,因此此处采用<code>CTR</code>,其对加密文本没有长度限制。</p> +<h2 id="前端实现">前端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端实现">后端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="示例代码">示例代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64encode</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Util</span> <span class="kn">import</span> <span class="n">Counter</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">codecs</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">hashlib</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">convert_to_md5</span><span class="p">(</span><span class="n">info</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> md5加密 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param info: 需要加密的内容 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: md5加密密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">md5</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">info</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">md5</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesCreateKey</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成aes加密的key,key的长度必须16位 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回key的base64密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">en_key</span> <span class="o">=</span> <span class="n">convert_to_md5</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">100000</span><span class="p">,</span> <span class="mi">999999</span><span class="p">)))[</span><span class="mi">8</span><span class="p">:</span><span class="o">-</span><span class="mi">8</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">b64encode</span><span class="p">(</span><span class="n">en_key</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto" target="_blank" rel="noopener" + >https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto</a></li> +</ul> + + + + nginx报错an upstream response is buffered to a temporary file + https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/ + Sat, 15 Aug 2020 22:07:58 +0000 + + https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/ + <h2 id="报错">报错</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">an upstream response is buffered to a temporary file +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p><code>Nginx</code>配置加上如下配置</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">proxy_max_temp_file_size 0<span class="p">;</span> +</span></span><span class="line"><span class="cl">client_max_body_size 50m<span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Golang使用JSON格式存取Redis + https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/ + Tue, 11 Aug 2020 18:42:42 +0000 + + https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/ + <h2 id="前言">前言</h2> +<p>对于<code>Golang</code>操作<code>Redis</code>,此处使用<code>github.com/go-redis/redis</code>。</p> +<h2 id="操作">操作</h2> +<h3 id="连接redis服务器">连接redis服务器</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">redis</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsoniter</span> <span class="s">&#34;github.com/json-iterator/go&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BaseClient</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="o">:=</span> <span class="s">&#34;redis_server&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="o">:=</span> <span class="s">&#34;redis_port&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="o">:=</span> <span class="s">&#34;redis_password&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>###存储</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">SetJson</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">value</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{},</span> <span class="nx">expiration</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">BaseClient</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">valueString</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">jsoniter</span><span class="p">.</span><span class="nf">MarshalToString</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">valueString</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="nx">expiration</span><span class="p">)</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">).</span><span class="nf">Err</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>调用</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">SetJson</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">,</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;name&#34;</span><span class="p">:</span> <span class="s">&#34;tom&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;age&#34;</span><span class="p">:</span> <span class="mi">12</span> +</span></span><span class="line"><span class="cl"> <span class="p">}),</span> +</span></span><span class="line"><span class="cl"> <span class="mi">60</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>###读取</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">value</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">BaseClient</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">key</span><span class="p">).</span><span class="nf">Result</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>调用</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Age</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">user</span> <span class="nx">User</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">value</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>使用<code>JSON</code>格式存储与读取其实就是对目标数据在存储前和读取后进行格式转换。</p> + + + + Go生成6位随机数 + https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/ + Tue, 11 Aug 2020 09:08:20 +0000 + + https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/ + <div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;math/rand&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;strconv&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">CreateVerifyCode</span><span class="p">()</span> <span class="p">(</span><span class="nx">verifyCode</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="o">:=</span> <span class="mi">100000</span> +</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="o">:=</span> <span class="mi">999999</span> +</span></span><span class="line"><span class="cl"> <span class="nx">verifyCode</span> <span class="p">=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Itoa</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="nx">max</span><span class="o">-</span><span class="nx">min</span><span class="p">)</span> <span class="o">+</span> <span class="nx">min</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Vue按需引入ElementUI报错Error -- Plugin -- Preset files are not allowed to export objects, only functions + https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/ + Mon, 10 Aug 2020 00:00:54 +0000 + + https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/ + <h2 id="报错">报错</h2> +<p>按照ElementUI官方文档按需引入却报错,首先报错缺少<code>babel-preset-es2015</code>。安装该组件之后编译却报错。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Error: Plugin/Preset files are not allowed to <span class="nb">export</span> objects, only functions. +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>该问题为<code>babel</code>版本冲突。</p> +<h3 id="安装插件">安装插件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add @babel/preset-env +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑babelrc">编辑<code>.babelrc</code></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;presets&#34;</span><span class="o">:</span> <span class="p">[[</span><span class="s2">&#34;@babel/preset-env&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="s2">&#34;modules&#34;</span><span class="o">:</span> <span class="kc">false</span> <span class="p">}]],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;plugins&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;component&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;libraryName&#34;</span><span class="o">:</span> <span class="s2">&#34;element-ui&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;styleLibraryName&#34;</span><span class="o">:</span> <span class="s2">&#34;theme-chalk&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask_RESTful解析常见类型请求数据 + https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/ + Wed, 05 Aug 2020 15:49:56 +0000 + + https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/ + <h2 id="前言">前言</h2> +<p><code>Flask_RESTful</code>是一个Flask 扩展,它添加了快速构建 REST APIs 的支持。其请求解析接口是模仿 <code>argparse</code> 接口。它设计成提供简单并且统一的访问 Flask 中 <code>flask.request</code> 对象里的任何变量的入口。</p> +<br /> +<h2 id="常见类型解析">常见类型解析</h2> +<h3 id="基本参数">基本参数</h3> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="s2">&#34;heihei&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="必选参数">必选参数</h3> +<p>使用参数<code>required</code>。</p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="s2">&#34;heihei&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="列表string">列表[string]</h3> +<p>使用参数<code>action = 'append'</code></p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;handsome&#34;</span><span class="p">,</span> <span class="s2">&#34;cheerful&#34;</span><span class="p">,</span> <span class="s2">&#34;optimism&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">action</span> <span class="o">=</span> <span class="s1">&#39;append&#39;</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="列表dict">列表[dict]</h3> +<p>使用参数<code>action = 'append'</code></p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;friends&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;tom&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;jerry&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">action</span> <span class="o">=</span> <span class="s1">&#39;append&#39;</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="json">JSON</h3> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;character&#34;</span><span class="p">:</span> <span class="s2">&#34;optimism&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Go http请求报错x509 certificate signed by unknown authority + https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/ + Tue, 04 Aug 2020 09:58:55 +0000 + + https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/ + <h2 id="报错">报错</h2> +<p>在Go中<code>POST</code>请求时报错</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">x509: certificate signed by unknown authority +</span></span></code></pre></td></tr></table> +</div> +</div><p>即无法检验证书。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Handle</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>跳过校验即可。此处引入<code>&quot;crypto/tls&quot;</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;crypto/tls&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Handle</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">tr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Transport</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TLSClientConfig</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">tls</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="nx">InsecureSkipVerify</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">client</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Timeout</span><span class="p">:</span> <span class="mi">15</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Transport</span><span class="p">:</span> <span class="nx">tr</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + SFTP部署报错解决记录 + https://blog.hunterji.com/p/sftp%E9%83%A8%E7%BD%B2%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E8%AE%B0%E5%BD%95/ + Thu, 23 Jul 2020 16:56:30 +0000 + + https://blog.hunterji.com/p/sftp%E9%83%A8%E7%BD%B2%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E8%AE%B0%E5%BD%95/ + <h2 id="前言">前言</h2> +<p>部署SFTP服务器,数次遇到几个报错,特此记录</p> +<h2 id="环境">环境</h2> +<ul> +<li> +<p>路径</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">home +</span></span><span class="line"><span class="cl">└── tom +</span></span><span class="line"><span class="cl"> └── uploads +</span></span></code></pre></td></tr></table> +</div> +</div></li> +<li> +<p>用户为<code>tom</code></p> +</li> +</ul> +<h2 id="报错">报错</h2> +<h3 id="报错一">报错一</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">permission denied +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="报错二">报错二</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bad ownership or modes <span class="k">for</span> chroot directory component <span class="s2">&#34;/home&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>以上两个报错,此处为统一解决。</p> +<ul> +<li>创建用户组</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">groupadd ftp +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>将用户加入用户组</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">usermod -a -G ftp tom +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>设置权限</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chown root:ftp -R /home/tom +</span></span><span class="line"><span class="cl">chown tom:ftp -R /home/tom/uploads +</span></span><span class="line"><span class="cl">chmod <span class="m">755</span> -R /home +</span></span></code></pre></td></tr></table> +</div> +</div> + + + flask_sqlalchemy一对一、一对多、多对一、多对多 + https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/ + Tue, 23 Jun 2020 13:46:21 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/ + <h2 id="一对多">一对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Country</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">capital</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Capital&#39;</span><span class="p">,</span> <span class="n">uselist</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Capital</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">country_id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s1">&#39;country.id&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">country</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Country&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="一对多-1">一对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">phone</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Article</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">title</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="n">index</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">body</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Text</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多对一">多对一</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Citizen</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">city_id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s1">&#39;city.id&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">city</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;City&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">City</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多对多">多对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">association_table</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Table</span><span class="p">(</span><span class="s1">&#39;association&#39;</span><span class="p">,</span><span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s1">&#39;student_id&#39;</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">Foreign</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Student</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">grade</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">teachers</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Teacher&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">secondary</span><span class="o">=</span><span class="n">association_table</span><span class="p">,</span> <span class="n">back_populates</span><span class="o">=</span><span class="s1">&#39;students&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Teacher</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">office</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + docker中php上传大小限制 + https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/ + Wed, 03 Jun 2020 15:35:33 +0000 + + https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/ + <h2 id="问题">问题</h2> +<p>使用<code>docker</code>运行的<code>wordpress</code>,有报错<code>The uploaded file exceeds the upload_max_filesize directive in php.ini</code>。</p> +<h2 id="过程">过程</h2> +<p>按照一般的方式去修改文件<code>php.ini</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">upload_max_filesize</span> <span class="o">=</span> 30M +</span></span><span class="line"><span class="cl"><span class="nv">post_max_size</span> <span class="o">=</span> 30M +</span></span></code></pre></td></tr></table> +</div> +</div><p>容器中有两个<code>php.ini</code>文件:</p> +<ul> +<li>php.ini-development</li> +<li>php.ini-production</li> +</ul> +<p>都修改之后却并不生效。</p> +<h2 id="解决">解决</h2> +<p>在文件夹<code>conf.d</code>中添加文件<code>uploads.ini</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">file_uploads = On +</span></span><span class="line"><span class="cl">memory_limit = 64M +</span></span><span class="line"><span class="cl">upload_max_filesize = 64M +</span></span><span class="line"><span class="cl">post_max_size = 64M +</span></span><span class="line"><span class="cl">max_execution_time = 600 +</span></span></code></pre></td></tr></table> +</div> +</div><p>文件夹<code>conf.d</code>位置需要看具体环境:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php -i <span class="p">|</span> grep php.ini +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后在其目录下找到文件夹<code>conf.d</code>。</p> +<p>也可以通过在运行容器时候直接将该文件映射进入。</p> + + + + Linux分享文件?快速创建静态文件服务器 + https://blog.hunterji.com/p/linux%E5%88%86%E4%BA%AB%E6%96%87%E4%BB%B6%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BA%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/ + Tue, 02 Jun 2020 19:05:56 +0000 + + https://blog.hunterji.com/p/linux%E5%88%86%E4%BA%AB%E6%96%87%E4%BB%B6%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BA%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/ + <h2 id="需求">需求</h2> +<p>Linux对于开发者来说极其友好,但是由于国内主流办公产品相关的生态较为匮乏,因此如何使用Linux去分享文件是一件十分头疼的问题。</p> +<p>对于这个问题,可以直接使用静态文件服务器解决部分需求,如下介绍几个常见方法。</p> +<h2 id="语言类">语言类</h2> +<h3 id="python">Python</h3> +<p>对于<code>Python</code>来说,可以直接使用内置的库来实现。</p> +<ul> +<li> +<p>python2</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">SimpleHTTPServer</span> <span class="mi">8000</span> +</span></span></code></pre></td></tr></table> +</div> +</div></li> +<li> +<p>Python3</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">http</span><span class="o">.</span><span class="n">server</span> <span class="mi">8000</span> +</span></span></code></pre></td></tr></table> +</div> +</div></li> +</ul> +<h3 id="nodejs">Node.js</h3> +<p><code>node</code>生态内有一个项目<code>http-server</code>,直接<code>V8</code>引擎带你飞。</p> +<ol> +<li>安装</li> +</ol> +<ul> +<li>Npm</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install --global http-server +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>Homebrew</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew install http-server +</span></span></code></pre></td></tr></table> +</div> +</div><ol start="2"> +<li>运行</li> +</ol> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">http-server <span class="o">[</span>path<span class="o">]</span> <span class="o">[</span>options<span class="o">]</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>例如:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> exmaple/ +</span></span><span class="line"><span class="cl">http-server +</span></span></code></pre></td></tr></table> +</div> +</div><ol start="3"> +<li>项目仓库地址</li> +</ol> +<p><a class="link" href="https://github.com/http-party/http-server" target="_blank" rel="noopener" + >https://github.com/http-party/http-server</a></p> +<h2 id="服务类">服务类</h2> +<ol> +<li>Nginx/Apache</li> +</ol> +<p><code>Nginx</code>和<code>Apache</code>本身可用于静态文件服务器,这就需要用户直接在本地安装。</p> +<p>当然,<code>nginx</code>需要注意配置一下,打开索引:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server { +</span></span><span class="line"><span class="cl"> listen 80; +</span></span><span class="line"><span class="cl"> ... +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> location / { +</span></span><span class="line"><span class="cl"> root /usr/share/nginx/html; +</span></span><span class="line"><span class="cl"> autoindex on; +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">} +</span></span></code></pre></td></tr></table> +</div> +</div><ol start="2"> +<li>Docker</li> +</ol> +<p>使用<code>Docker</code>其实也是使用如<code>Nginx</code>来实现静态文件服务器,但是容器化在该场景存在几大优势:</p> +<ul> +<li>即开即用</li> +<li>环境隔离</li> +</ul> +<p>相对于直接安装<code>Nginx</code>或者<code>Apache</code>,更推荐使用<code>Docker</code>。</p> + + + + vue+flask前后端信息传输非对称加密 + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + Fri, 22 May 2020 15:11:05 +0000 + + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + <h2 id="介绍">介绍</h2> +<p>为了前后端传输数据的安全性,需要对数据进行加密。因此选定使用非对称加密,此处为<code>RSA</code>。</p> +<p>在传输数据前,后端生成公钥和私钥,将公钥给前端,前端加密之后,将密文传给后端,后端使用私钥解密即可得到原始的数据。</p> +<h2 id="环境">环境</h2> +<ul> +<li><strong>前端</strong>使用<code>jsencrypt</code>加密</li> +<li><strong>后端</strong>使用<code>Crypto</code>生成密钥和解密</li> +</ul> +<h2 id="后端生成密钥">后端生成密钥</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="前端用公钥加密">前端用公钥加密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端使用私钥解密">后端使用私钥解密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>这里使用<code>base64</code>先解密一遍是必要的,否则报错<code>ValueError: Ciphertext with incorrect length.</code></li> +</ul> +<h2 id="完整代码">完整代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_encrypt</span><span class="p">(</span><span class="n">public_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa加密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params publick_key: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 需要加密的信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">public_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">encrypt</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="分段加密">分段加密</h2> +<p><code>RSA</code>加密信息最长为<code>128</code>位,过长则会报错,因此,对于过长的信息需要分段加密,后端也要分段解密后拼装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">en_str</span><span class="p">(</span><span class="nx">target_str</span><span class="p">,</span> <span class="nx">pubkey</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">en_array</span> <span class="o">=</span> <span class="p">[];</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">n</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">// 每段信息的长度 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">l</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">l</span> <span class="o">/</span> <span class="nx">n</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">message</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">n</span> <span class="o">*</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">n</span> <span class="o">*</span> <span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="nx">en_array</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">en_array</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + v-charts添加图表标题 + https://blog.hunterji.com/p/v-charts%E6%B7%BB%E5%8A%A0%E5%9B%BE%E8%A1%A8%E6%A0%87%E9%A2%98/ + Thu, 26 Mar 2020 14:54:27 +0000 + + https://blog.hunterji.com/p/v-charts%E6%B7%BB%E5%8A%A0%E5%9B%BE%E8%A1%A8%E6%A0%87%E9%A2%98/ + <h2 id="场景">场景</h2> +<p>使用 <code>v-charts</code> 做数据可视化,需要给图表添加标题。</p> +<h2 id="解决方法">解决方法</h2> +<p>v-charts本身并没有提供显示标题的配置,顾需要引入 <code>echarts</code> 的 <code>title</code> 。</p> +<h2 id="实现">实现</h2> +<h3 id="引入title">引入title</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;echarts/lib/component/title&#34;</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加标题配置">添加标题配置</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">chartTitle</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;平台用户与创客数量对比图&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">color</span><span class="o">:</span> <span class="s2">&#34;white&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ve-bar</span> <span class="na">:title</span><span class="o">=</span><span class="s">&#34;chartTitle&#34;</span> <span class="p">/&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="完整实现">完整实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">ve-bar</span> <span class="na">...</span> <span class="nt">:title</span><span class="s">=&#34;chartTitle&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;echarts/lib/component/title&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">chartTitle</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;平台用户与创客数量对比图&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">textStyle</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fontWeight</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">color</span><span class="o">:</span> <span class="s2">&#34;white&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="echarts配置手册">echarts配置手册</h2> +<ul> +<li><a class="link" href="https://www.echartsjs.com/zh/option.html#title" target="_blank" rel="noopener" + >https://www.echartsjs.com/zh/option.html#title</a></li> +</ul> +<h2 id="参考文章">参考文章</h2> +<ul> +<li><a class="link" href="https://github.com/ElemeFE/v-charts/issues/191" target="_blank" rel="noopener" + >https://github.com/ElemeFE/v-charts/issues/191</a></li> +</ul> + + + + vue组件props双向绑定 + https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/ + Sat, 21 Mar 2020 23:48:37 +0000 + + https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/ + <p>在vue2中不允许子组件直接修改<code>props</code>,为单项数据流,所有若要修改只能通过额外的值,并监听<code>props</code>以改变额外的值。</p> +<h2 id="设置props">设置props</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">props</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="创建额外的值">创建额外的值</h2> +<p>在<code>data</code>中创建一个<code>localDialog</code>,其值为<code>this.dialog</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">localDialog</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="监听">监听</h2> +<p>保持同步的关键在于需要在子组件内监听<code>props</code>,即此处的<code>dialog</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">watch</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="子组件向父组件传递">子组件向父组件传递</h2> +<p>子组件使用<code>this.$emit()</code>即可向父组件传递变化的值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sendToFather</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s1">&#39;dialogchange&#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="父组件调用">父组件调用</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">your-component</span> <span class="na">:dialog</span><span class="o">=</span><span class="s">&#34;dialog&#34;</span> <span class="err">@</span><span class="na">dialogchange</span><span class="o">=</span><span class="s">&#34;dialogchange&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl">data() { +</span></span><span class="line"><span class="cl"> return { +</span></span><span class="line"><span class="cl"> dialog: false +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">}, +</span></span><span class="line"><span class="cl">methods: { +</span></span><span class="line"><span class="cl"> dialogchange(val) { +</span></span><span class="line"><span class="cl"> this.dialog = val +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">} +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整代码">完整代码</h2> +<h3 id="子组件">子组件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">div</span> <span class="o">:</span><span class="nx">visible</span><span class="o">=</span><span class="s2">&#34;localDialog&#34;</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">hunterji</span><span class="p">.</span><span class="nx">com</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">button</span> <span class="err">@</span><span class="nx">click</span><span class="o">=</span><span class="s2">&#34;sendToFather&#34;</span> <span class="o">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="err">/div&gt;</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/template&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">localDialog</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">watch</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sendToFather</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s1">&#39;dialogchange&#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="父组件">父组件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">component</span> <span class="o">:</span><span class="nx">dialog</span><span class="o">=</span><span class="s2">&#34;dialog&#34;</span> <span class="err">@</span><span class="nx">dialogchange</span><span class="o">=</span><span class="s2">&#34;dialogchange&#34;</span> <span class="o">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/template&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">yourComponent</span> <span class="nx">from</span> <span class="s1">&#39;./yourComponent&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">components</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">yourComponent</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialogchange</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask_sqlalchemy的增删改查 + https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/ + Fri, 20 Mar 2020 12:19:52 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/ + <h2 id="增">增</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;article1&#39;</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s1">&#39;heihei&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>若要在新增之后获取数据库中新增数据的信息,如<code>id</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;article1&#39;</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s1">&#39;heihei&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span> <span class="c1"># 添加这一条,用于预提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 输出新增数据的信息</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="删">删</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1">##把需要删除的数据查找出来</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">content</span> <span class="o">=</span> <span class="s1">&#39;heihei&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##把这条数据删除掉</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="改">改</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1">##先把你要更改的数据查找出来</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s1">&#39;article1&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##把这条数据需要修改的地方进行修改</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article2&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="查">查</h3> +<h3 id="查询单个">查询单个</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> <span class="c1"># 或者</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article1</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article1</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="查询所有">查询所有</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">article1</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="倒叙">倒叙</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">desc</span><span class="p">())</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="限制数量">限制数量</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">limit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue实现纯前端导入与解析excel表格文件 + https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/ + Thu, 19 Mar 2020 13:11:01 +0000 + + https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/ + <h2 id="场景">场景</h2> +<p>前端导入excel表格,直接前端解析文件,将数据传给后端。</p> +<h2 id="需要的库">需要的库</h2> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install xlsx +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">XLSX</span> <span class="nx">from</span> <span class="s2">&#34;xlsx&#34;</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="代码实现">代码实现</h2> +<h3 id="html部分">html部分</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> {{ upload_file || &#34;导入&#34; }} +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">input</span> +</span></span><span class="line"><span class="cl"> <span class="na">type</span><span class="o">=</span><span class="s">&#34;file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">accept</span><span class="o">=</span><span class="s">&#34;.xls,.xlsx&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;upload_file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="err">@</span><span class="na">change</span><span class="o">=</span><span class="s">&#34;readExcel($event)&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js部分">JS部分</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">readExcel</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 读取表格文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">let</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="sr">/\.(xls|xlsx)$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()))</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$message</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;上传格式不正确,请上传xls或者xlsx格式&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;warning&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 更新获取文件名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">upload_file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">ev</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">workbook</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;binary&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">wsname</span> <span class="o">=</span> <span class="nx">workbook</span><span class="p">.</span><span class="nx">SheetNames</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//取第一张表 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">utils</span><span class="p">.</span><span class="nx">sheet_to_json</span><span class="p">(</span><span class="nx">workbook</span><span class="p">.</span><span class="nx">Sheets</span><span class="p">[</span><span class="nx">wsname</span><span class="p">]);</span> <span class="c1">//生成json表格内容 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">ws</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsBinaryString</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="整体代码">整体代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span><span class="lnt">71 +</span><span class="lnt">72 +</span><span class="lnt">73 +</span><span class="lnt">74 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">{{</span> <span class="nx">upload_file</span> <span class="o">||</span> <span class="s2">&#34;导入&#34;</span> <span class="p">}}</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">input</span> +</span></span><span class="line"><span class="cl"> <span class="na">type</span><span class="o">=</span><span class="s">&#34;file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">accept</span><span class="o">=</span><span class="s">&#34;.xls,.xlsx&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;upload_file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nt">@change</span><span class="s">=&#34;readExcel($event)&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">XLSX</span> <span class="nx">from</span> <span class="s2">&#34;xlsx&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">upload_file</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lists</span><span class="o">:</span> <span class="p">[]</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">submit_form</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 给后端发送请求,更新数据 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;假装给后端发了个请求...&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">readExcel</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 读取表格文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">let</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="sr">/\.(xls|xlsx)$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()))</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$message</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;上传格式不正确,请上传xls或者xlsx格式&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;warning&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 更新获取文件名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">upload_file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">ev</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">workbook</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;binary&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">wsname</span> <span class="o">=</span> <span class="nx">workbook</span><span class="p">.</span><span class="nx">SheetNames</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//取第一张表 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">utils</span><span class="p">.</span><span class="nx">sheet_to_json</span><span class="p">(</span><span class="nx">workbook</span><span class="p">.</span><span class="nx">Sheets</span><span class="p">[</span><span class="nx">wsname</span><span class="p">]);</span> <span class="c1">//生成json表格内容 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">lists</span> <span class="o">=</span> <span class="p">[];</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 从解析出来的数据中提取相应的数据 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ws</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">item</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">that</span><span class="p">.</span><span class="nx">lists</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">username</span><span class="o">:</span> <span class="nx">item</span><span class="p">[</span><span class="s2">&#34;用户名&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nx">phone_number</span><span class="o">:</span> <span class="nx">item</span><span class="p">[</span><span class="s2">&#34;手机号&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 给后端发请求 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">submit_form</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsBinaryString</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="样式">样式</h2> +<p>原本的文件上传样式可能会跟页面整体风格不搭,所以需要修改其样式。不过此处并不是直接修改其样式而是通过写一个<code>div</code>来覆盖原有的上传按钮。此处样式与<code>element UI</code>中的<code>primary</code>按钮样式相同。</p> +<p>实现该样式的关键在于<code>.upload_file</code>的<code>opacity</code>和<code>position</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">border</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">4</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#409eff</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">40</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">margin-top</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">15</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">min-width</span><span class="p">:</span> <span class="mi">80</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="err">*</span><span class="n">zoom</span><span class="p">:</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">upload_file</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">font-size</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">opacity</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">filter</span><span class="p">:</span> <span class="nf">alpha</span><span class="p">(</span><span class="n">opacity</span><span class="err">=</span><span class="mi">0</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">60</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="最后">最后</h2> +<p>前端的日益强大导致很多功能都可以在前端去直接实现,并且可以减少服务器压力。</p> +<p>当然单纯地去实现这样的数据传输,尤其对于重要数据,是很不安全的,因此在前后端数据传输的时候,可以加上加密校验,这个后期会来写的。</p> +<h2 id="参考文章">参考文章</h2> +<p>为了实现该功能参考了如下大佬的文章:</p> +<ul> +<li><a class="link" href="https://www.wikimoe.com/?post=157" target="_blank" rel="noopener" + >【Vue 笔记】Vue 读取excel数据并生成数组</a></li> +<li><a class="link" href="https://qxiaomay.github.io/2018/07/13/vue%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E5%B9%B6%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97/" target="_blank" rel="noopener" + >vue前端导入并解析excel表格操作指南</a></li> +<li><a class="link" href="https://www.haorooms.com/post/css_input_uploadmh" target="_blank" rel="noopener" + >css input[type=file] 样式美化,input上传按钮美化</a></li> +</ul> + + + + Flask_sqlalchemy报错MySQL server has gone away解决 + https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/ + Thu, 12 Mar 2020 21:44:36 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/ + <h3 id="报错">报错</h3> +<p>使用<code>flask_sqlalchemy</code>,服务端出现500错误,日志显示报错如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">MySQL server has gone away +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解决">解决</h3> +<p>添加配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">SQLALCHEMY_POOL_RECYCLE</span> <span class="o">=</span> <span class="mi">280</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>该配置作用是设置多少秒后回收连接,如果不提供值,默认为 2 小时。此处将其设置为280秒。</p> +<p>该配置原文解释:</p> +<blockquote> +<p>Number of seconds after which a connection is automatically recycled. This is required for MySQL, which removes connections after 8 hours idle by default. Note that Flask-SQLAlchemy automatically sets this to 2 hours if MySQL is used.</p> +</blockquote> + + + + Flask_Restful视图函数模块化 + https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/ + Sun, 23 Feb 2020 14:55:36 +0000 + + https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/ + <p>Restful作为目前流行的api设计规范,在flask上也有较好的实践,即为flask_restful。我们在使用flask_restful的时候,当代码量达到一定程度,需要将视图函数模块化。然而在Flask之前一直使用Blueprint模块化,那么flask_restful如何模块化呢?底下就来瞅瞅!</p> +<h2 id="当前架构">当前架构</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views.py +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li><code>__init__.py</code>:将该文件夹标示为一个模块</li> +<li><code>commands.py</code>:额外命令,如初始化数据库</li> +<li><code>config.py</code>:配置文件</li> +<li><code>views.py</code>:视图函数</li> +</ul> +<p>此为一个简单的flask项目架构,我们可以将flask中的各个功能拆分出来,分给不同文件,最后在<code>__init__.py</code>导入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_cors</span> <span class="kn">import</span> <span class="n">CORS</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_restful</span> <span class="kn">import</span> <span class="n">Api</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="s1">&#39;server&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="s1">&#39;config.py&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">CORS</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">api</span> <span class="o">=</span> <span class="n">Api</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span><span class="p">,</span> <span class="n">views</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="初步模块化">初步模块化</h2> +<p>现在将视图函数改为一个文件夹<code>views</code>,然后在该文件夹下放入拆分后的文件。这里以登录和获取用户信息接口为例,架构就变成了下面这样。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views +</span></span><span class="line"><span class="cl"> ├── __init__.py +</span></span><span class="line"><span class="cl"> ├── login.py +</span></span><span class="line"><span class="cl"> └── info.py +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处的<code>__init__.py</code>文件功能如上,是将文件夹<code>views</code>标示为模块,但是此处的<code>__init__py</code>是个空白文件,但是<code>server</code>文件夹中的<code>__init__.py</code>的引入需要改变。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server.views</span> <span class="kn">import</span> <span class="n">login</span><span class="p">,</span> <span class="n">info</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="最终模块化">最终模块化</h2> +<p>此时还不够,例如登录和获取用户信息接口是<code>User</code>模块下的,而获取文章标题接口是<code>Article</code>模块的下的,因此我们继续分。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views +</span></span><span class="line"><span class="cl"> ├── Aricle +</span></span><span class="line"><span class="cl"> │   ├── __init__.py +</span></span><span class="line"><span class="cl"> │   └── title.py +</span></span><span class="line"><span class="cl"> ├── User +</span></span><span class="line"><span class="cl"> │   ├── __init__.py +</span></span><span class="line"><span class="cl"> │   ├── info.py +</span></span><span class="line"><span class="cl"> │   └── login.py +</span></span><span class="line"><span class="cl"> └── __init__.py +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>server</code>文件夹中的<code>__init__.py</code>的引入继续改变。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server.views.User</span> <span class="kn">import</span> <span class="n">login</span><span class="p">,</span> <span class="n">info</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">srever.views.Article</span> <span class="kn">import</span> <span class="n">title</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>至此,关于<code>flask_restful</code>的视图函数模块化结束,若有后续改进会继续分享,若大家有更好的方式请务必分享一下。</p> +<p>当时为了解决这个问题查了很久,但是要么是介绍blueprint的,要么就是flask_restful插件入门,简直刺激&hellip;</p> + + + + vue2实现实时生成二维码和将网页合成图片并在微信内置浏览器长按保存 + https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/ + Sat, 22 Feb 2020 22:43:24 +0000 + + https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/ + <h2 id="url转为二维码">url转为二维码</h2> +<h3 id="需要的库">需要的库</h3> +<p><a class="link" href="https://www.npmjs.com/package/qrcodejs2" target="_blank" rel="noopener" + >qrcodejs2</a></p> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install qrcodejs2 --save +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="实现">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;qrcode&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">GenerateQRcode</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nx">QRCode</span><span class="p">(</span><span class="s2">&#34;qrcode&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="c1">// 此处的qrcode为上面div的id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">text</span><span class="o">:</span> <span class="nx">目标url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorDark</span><span class="o">:</span> <span class="s2">&#34;#000000&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorLight</span><span class="o">:</span> <span class="s2">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">correctLevel</span><span class="o">:</span> <span class="nx">QRCode</span><span class="p">.</span><span class="nx">CorrectLevel</span><span class="p">.</span><span class="nx">H</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">GenerateQRcode</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="网页保存为图片">网页保存为图片</h2> +<h3 id="需要的库-1">需要的库</h3> +<p><a class="link" href="https://github.com/hongru/canvas2image" target="_blank" rel="noopener" + >html2canvas</a></p> +<h3 id="安装-1">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install html2canvas --save +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="实现-1">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputImg</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetDom</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;container&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">html2canvas</span><span class="p">(</span><span class="nx">targetDom</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">canvas</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">outputImg</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="整合">整合</h2> +<p>关于小程序内置浏览器的图片下载,需要一个用来生成图片的块,还需要一个<code>img</code>,先将其隐藏。实现步骤就是首先生成二维码,然后再将html生成图片,最后在html2canvas回调中替换<code>img</code>的<code>src</code>,并将生成图片的块隐藏,将<code>img</code>显示。</p> +<p>当然关于这个实现方式,我看到的技术分享文章中,还有两种不同的解决方式:</p> +<ul> +<li>不需要html来写生成图片的块,而是使用js直接创建;</li> +<li>不需要替换隐藏,将生成的图片覆盖到html生成图片的块之前;</li> +</ul> +<p>这里我只记录一下我使用的,后期会再去研究这两种实现方式。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!--</span><span class="nx">合成的图片</span><span class="err">,</span><span class="nx">默认隐藏</span><span class="err">,</span><span class="nx">合成之后显示</span><span class="o">--&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-show</span><span class="s">=&#34;imgUrl.length&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="nt">:src</span><span class="s">=&#34;imgUrl&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;生成的图片&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;image&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!--</span><span class="nx">合成图片需要的html块</span><span class="err">,</span><span class="nx">默认显示</span><span class="err">,</span><span class="nx">合成之后隐藏</span><span class="o">--&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;container&#34;</span> <span class="nt">v-show</span><span class="s">=&#34;!imgUrl.length&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;qrcode&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span><span class="nx">长按识别二维码</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">imgUrl</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputImg</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetDom</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;container&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">html2canvas</span><span class="p">(</span><span class="nx">targetDom</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">canvas</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 将图片src替换为canvas生成之后转换的url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">imgUrl</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">GenerateQRcode</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nx">QRCode</span><span class="p">(</span><span class="s2">&#34;qrcode&#34;</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">:</span> <span class="nx">目标url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorDark</span><span class="o">:</span> <span class="s2">&#34;#000000&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorLight</span><span class="o">:</span> <span class="s2">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">correctLevel</span><span class="o">:</span> <span class="nx">QRCode</span><span class="p">.</span><span class="nx">CorrectLevel</span><span class="p">.</span><span class="nx">H</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="nx">resolve</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 先生成二维码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">GenerateQRcode</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resove</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 再合成图片 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">outputImg</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 生成之后的图片有点放肆,可以设置宽度来适应手机屏幕 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">.</span><span class="nx">image</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">100</span><span class="o">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>由此即可实现需要的功能了。</p> +<p>关于后续的优化,需要解决的图片清晰度问题、跨域图片问题等,可以参考<a class="link" href="https://segmentfault.com/a/1190000011478657" target="_blank" rel="noopener" + >这篇文章</a>,这位大佬写得很详细。</p> + + + + + diff --git a/post/page/1/index.html b/post/page/1/index.html new file mode 100644 index 0000000..0cd8ae8 --- /dev/null +++ b/post/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/post/ + + + + + + diff --git a/post/page/2/index.html b/post/page/2/index.html new file mode 100644 index 0000000..b9dcfe2 --- /dev/null +++ b/post/page/2/index.html @@ -0,0 +1,696 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

75 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/post/page/3/index.html b/post/page/3/index.html new file mode 100644 index 0000000..a00adfa --- /dev/null +++ b/post/page/3/index.html @@ -0,0 +1,696 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

75 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/post/page/4/index.html b/post/page/4/index.html new file mode 100644 index 0000000..3c60c2c --- /dev/null +++ b/post/page/4/index.html @@ -0,0 +1,699 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

75 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/post/page/5/index.html b/post/page/5/index.html new file mode 100644 index 0000000..222b247 --- /dev/null +++ b/post/page/5/index.html @@ -0,0 +1,696 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

75 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/post/page/6/index.html b/post/page/6/index.html new file mode 100644 index 0000000..2b3eb2e --- /dev/null +++ b/post/page/6/index.html @@ -0,0 +1,696 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

75 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/post/page/7/index.html b/post/page/7/index.html new file mode 100644 index 0000000..d93ed11 --- /dev/null +++ b/post/page/7/index.html @@ -0,0 +1,597 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

75 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/scss/style.min.abbd69b2908fdfcd5179898beaafd374514a86538d81639ddd2c58c06ae54e40.css b/scss/style.min.abbd69b2908fdfcd5179898beaafd374514a86538d81639ddd2c58c06ae54e40.css new file mode 100644 index 0000000..bb25dfd --- /dev/null +++ b/scss/style.min.abbd69b2908fdfcd5179898beaafd374514a86538d81639ddd2c58c06ae54e40.css @@ -0,0 +1,10 @@ +/*!* Hugo Theme Stack +* +* @author: Jimmy Cai +* @website: https://jimmycai.com +* @link: https://github.com/CaiJimmy/hugo-theme-stack*/:root{--main-top-padding:35px;--body-background:#f5f5fa;--accent-color:#34495e;--accent-color-darker:#2c3e50;--accent-color-text:#fff;--body-text-color:#707070;--tag-border-radius:4px;--section-separation:40px;--scrollbar-thumb:hsl(0, 0%, 85%);--scrollbar-track:var(--body-background)}@media(min-width:1280px){:root{--main-top-padding:50px}}:root[data-scheme=dark]{--body-background:#303030;--accent-color:#ecf0f1;--accent-color-darker:#bdc3c7;--accent-color-text:#000;--body-text-color:rgba(255, 255, 255, 0.7);--scrollbar-thumb:hsl(0, 0%, 40%);--scrollbar-track:var(--body-background)}:root{--sys-font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", "Droid Sans", "Helvetica Neue";--zh-font-family:"PingFang SC", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei";--base-font-family:"Lato", var(--sys-font-family), var(--zh-font-family), sans-serif;--code-font-family:Menlo, Monaco, Consolas, "Courier New", var(--zh-font-family), monospace}:root{--card-background:#fff;--card-background-selected:#eaeaea;--card-text-color-main:#000;--card-text-color-secondary:#747474;--card-text-color-tertiary:#767676;--card-separator-color:rgba(218, 218, 218, 0.5);--card-border-radius:10px;--card-padding:20px;--small-card-padding:25px 20px}@media(min-width:768px){:root{--card-padding:25px}}@media(min-width:1280px){:root{--card-padding:30px}}@media(min-width:768px){:root{--small-card-padding:25px}}:root[data-scheme=dark]{--card-background:#424242;--card-background-selected:rgba(255, 255, 255, 0.16);--card-text-color-main:rgba(255, 255, 255, 0.9);--card-text-color-secondary:rgba(255, 255, 255, 0.7);--card-text-color-tertiary:rgba(255, 255, 255, 0.5);--card-separator-color:rgba(255, 255, 255, 0.12)}:root{--article-font-family:var(--base-font-family);--article-font-size:1.6rem;--article-line-height:1.85}@media(min-width:768px){:root{--article-font-size:1.7rem}}:root{--blockquote-border-size:4px;--blockquote-background-color:rgb(248 248 248);--heading-border-size:4px;--link-background-color:189, 195, 199;--link-background-opacity:0.5;--link-background-opacity-hover:0.7;--pre-background-color:#272822;--pre-text-color:#f8f8f2;--code-background-color:rgba(0, 0, 0, 0.12);--code-text-color:#808080;--table-border-color:#dadada;--tr-even-background-color:#efefee;--kbd-border-color:#dadada}:root[data-scheme=dark]{--code-background-color:#272822;--code-text-color:rgba(255, 255, 255, 0.9);--table-border-color:#717171;--tr-even-background-color:#545454;--blockquote-background-color:rgb(75 75 75)}:root{--shadow-l1:0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04);--shadow-l2:0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);--shadow-l3:0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);--shadow-l4:0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), + 0px 0px 1px rgba(0, 0, 0, 0.04)}[data-scheme=light]{--pre-text-color:#272822;--pre-background-color:#fafafa}[data-scheme=light] .chroma{color:#272822;background-color:#fafafa}[data-scheme=light] .chroma .err{color:#960050}[data-scheme=light] .chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}[data-scheme=light] .chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:100%;display:block}[data-scheme=light] .chroma .lntable>tbody{display:block;width:100%}[data-scheme=light] .chroma .lntable>tbody>tr{display:flex;width:100%}[data-scheme=light] .chroma .lntable>tbody>tr>td:last-child{overflow-x:auto}[data-scheme=light] .chroma .hl{display:block;width:100%;background-color:#ffc}[data-scheme=light] .chroma .lnt{margin-right:.4em;padding:0 .4em;color:#7f7f7f;display:block}[data-scheme=light] .chroma .ln{margin-right:.4em;padding:0 .4em;color:#7f7f7f}[data-scheme=light] .chroma .k{color:#00a8c8}[data-scheme=light] .chroma .kc{color:#00a8c8}[data-scheme=light] .chroma .kd{color:#00a8c8}[data-scheme=light] .chroma .kn{color:#f92672}[data-scheme=light] .chroma .kp{color:#00a8c8}[data-scheme=light] .chroma .kr{color:#00a8c8}[data-scheme=light] .chroma .kt{color:#00a8c8}[data-scheme=light] .chroma .n{color:#111}[data-scheme=light] .chroma .na{color:#75af00}[data-scheme=light] .chroma .nb{color:#111}[data-scheme=light] .chroma .bp{color:#111}[data-scheme=light] .chroma .nc{color:#75af00}[data-scheme=light] .chroma .no{color:#00a8c8}[data-scheme=light] .chroma .nd{color:#75af00}[data-scheme=light] .chroma .ni{color:#111}[data-scheme=light] .chroma .ne{color:#75af00}[data-scheme=light] .chroma .nf{color:#75af00}[data-scheme=light] .chroma .fm{color:#111}[data-scheme=light] .chroma .nl{color:#111}[data-scheme=light] .chroma .nn{color:#111}[data-scheme=light] .chroma .nx{color:#75af00}[data-scheme=light] .chroma .py{color:#111}[data-scheme=light] .chroma .nt{color:#f92672}[data-scheme=light] .chroma .nv{color:#111}[data-scheme=light] .chroma .vc{color:#111}[data-scheme=light] .chroma .vg{color:#111}[data-scheme=light] .chroma .vi{color:#111}[data-scheme=light] .chroma .vm{color:#111}[data-scheme=light] .chroma .l{color:#ae81ff}[data-scheme=light] .chroma .ld{color:#d88200}[data-scheme=light] .chroma .s{color:#d88200}[data-scheme=light] .chroma .sa{color:#d88200}[data-scheme=light] .chroma .sb{color:#d88200}[data-scheme=light] .chroma .sc{color:#d88200}[data-scheme=light] .chroma .dl{color:#d88200}[data-scheme=light] .chroma .sd{color:#d88200}[data-scheme=light] .chroma .s2{color:#d88200}[data-scheme=light] .chroma .se{color:#ae81ff}[data-scheme=light] .chroma .sh{color:#d88200}[data-scheme=light] .chroma .si{color:#d88200}[data-scheme=light] .chroma .sx{color:#d88200}[data-scheme=light] .chroma .sr{color:#d88200}[data-scheme=light] .chroma .s1{color:#d88200}[data-scheme=light] .chroma .ss{color:#d88200}[data-scheme=light] .chroma .m{color:#ae81ff}[data-scheme=light] .chroma .mb{color:#ae81ff}[data-scheme=light] .chroma .mf{color:#ae81ff}[data-scheme=light] .chroma .mh{color:#ae81ff}[data-scheme=light] .chroma .mi{color:#ae81ff}[data-scheme=light] .chroma .il{color:#ae81ff}[data-scheme=light] .chroma .mo{color:#ae81ff}[data-scheme=light] .chroma .o{color:#f92672}[data-scheme=light] .chroma .ow{color:#f92672}[data-scheme=light] .chroma .p{color:#111}[data-scheme=light] .chroma .c{color:#75715e}[data-scheme=light] .chroma .ch{color:#75715e}[data-scheme=light] .chroma .cm{color:#75715e}[data-scheme=light] .chroma .c1{color:#75715e}[data-scheme=light] .chroma .cs{color:#75715e}[data-scheme=light] .chroma .cp{color:#75715e}[data-scheme=light] .chroma .cpf{color:#75715e}[data-scheme=light] .chroma .gd{color:#f92672}[data-scheme=light] .chroma .ge{font-style:italic}[data-scheme=light] .chroma .gi{color:#75af00}[data-scheme=light] .chroma .gs{font-weight:700}[data-scheme=light] .chroma .gu{color:#75715e}[data-scheme=dark]{--pre-text-color:#f8f8f2;--pre-background-color:#272822}[data-scheme=dark] .chroma{color:#f8f8f2;background-color:#272822}[data-scheme=dark] .chroma .err{color:#bb0064}[data-scheme=dark] .chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}[data-scheme=dark] .chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:100%;display:block}[data-scheme=dark] .chroma .lntable>tbody{display:block;width:100%}[data-scheme=dark] .chroma .lntable>tbody>tr{display:flex;width:100%}[data-scheme=dark] .chroma .lntable>tbody>tr>td:last-child{overflow-x:auto}[data-scheme=dark] .chroma .hl{display:block;width:100%;background-color:#ffc}[data-scheme=dark] .chroma .lnt{margin-right:.4em;padding:0 .4em;color:#7f7f7f;display:block}[data-scheme=dark] .chroma .ln{margin-right:.4em;padding:0 .4em;color:#7f7f7f}[data-scheme=dark] .chroma .k{color:#66d9ef}[data-scheme=dark] .chroma .kc{color:#66d9ef}[data-scheme=dark] .chroma .kd{color:#66d9ef}[data-scheme=dark] .chroma .kn{color:#f92672}[data-scheme=dark] .chroma .kp{color:#66d9ef}[data-scheme=dark] .chroma .kr{color:#66d9ef}[data-scheme=dark] .chroma .kt{color:#66d9ef}[data-scheme=dark] .chroma .n{color:#f8f8f2}[data-scheme=dark] .chroma .na{color:#a6e22e}[data-scheme=dark] .chroma .nb{color:#f8f8f2}[data-scheme=dark] .chroma .bp{color:#f8f8f2}[data-scheme=dark] .chroma .nc{color:#a6e22e}[data-scheme=dark] .chroma .no{color:#66d9ef}[data-scheme=dark] .chroma .nd{color:#a6e22e}[data-scheme=dark] .chroma .ni{color:#f8f8f2}[data-scheme=dark] .chroma .ne{color:#a6e22e}[data-scheme=dark] .chroma .nf{color:#a6e22e}[data-scheme=dark] .chroma .fm{color:#f8f8f2}[data-scheme=dark] .chroma .nl{color:#f8f8f2}[data-scheme=dark] .chroma .nn{color:#f8f8f2}[data-scheme=dark] .chroma .nx{color:#a6e22e}[data-scheme=dark] .chroma .py{color:#f8f8f2}[data-scheme=dark] .chroma .nt{color:#f92672}[data-scheme=dark] .chroma .nv{color:#f8f8f2}[data-scheme=dark] .chroma .vc{color:#f8f8f2}[data-scheme=dark] .chroma .vg{color:#f8f8f2}[data-scheme=dark] .chroma .vi{color:#f8f8f2}[data-scheme=dark] .chroma .vm{color:#f8f8f2}[data-scheme=dark] .chroma .l{color:#ae81ff}[data-scheme=dark] .chroma .ld{color:#e6db74}[data-scheme=dark] .chroma .s{color:#e6db74}[data-scheme=dark] .chroma .sa{color:#e6db74}[data-scheme=dark] .chroma .sb{color:#e6db74}[data-scheme=dark] .chroma .sc{color:#e6db74}[data-scheme=dark] .chroma .dl{color:#e6db74}[data-scheme=dark] .chroma .sd{color:#e6db74}[data-scheme=dark] .chroma .s2{color:#e6db74}[data-scheme=dark] .chroma .se{color:#ae81ff}[data-scheme=dark] .chroma .sh{color:#e6db74}[data-scheme=dark] .chroma .si{color:#e6db74}[data-scheme=dark] .chroma .sx{color:#e6db74}[data-scheme=dark] .chroma .sr{color:#e6db74}[data-scheme=dark] .chroma .s1{color:#e6db74}[data-scheme=dark] .chroma .ss{color:#e6db74}[data-scheme=dark] .chroma .m{color:#ae81ff}[data-scheme=dark] .chroma .mb{color:#ae81ff}[data-scheme=dark] .chroma .mf{color:#ae81ff}[data-scheme=dark] .chroma .mh{color:#ae81ff}[data-scheme=dark] .chroma .mi{color:#ae81ff}[data-scheme=dark] .chroma .il{color:#ae81ff}[data-scheme=dark] .chroma .mo{color:#ae81ff}[data-scheme=dark] .chroma .o{color:#f92672}[data-scheme=dark] .chroma .ow{color:#f92672}[data-scheme=dark] .chroma .p{color:#f8f8f2}[data-scheme=dark] .chroma .c{color:#75715e}[data-scheme=dark] .chroma .ch{color:#75715e}[data-scheme=dark] .chroma .cm{color:#75715e}[data-scheme=dark] .chroma .c1{color:#75715e}[data-scheme=dark] .chroma .cs{color:#75715e}[data-scheme=dark] .chroma .cp{color:#75715e}[data-scheme=dark] .chroma .cpf{color:#75715e}[data-scheme=dark] .chroma .gd{color:#f92672}[data-scheme=dark] .chroma .ge{font-style:italic}[data-scheme=dark] .chroma .gi{color:#a6e22e}[data-scheme=dark] .chroma .gs{font-weight:700}[data-scheme=dark] .chroma .gu{color:#75715e}:root{--menu-icon-separation:40px;--container-padding:15px;--widget-separation:var(--section-separation)}.container{margin-left:auto;margin-right:auto}.container .left-sidebar{order:-3;max-width:var(--left-sidebar-max-width)}.container .right-sidebar{order:-1;max-width:var(--right-sidebar-max-width)}@media(min-width:1024px){.container .right-sidebar{display:flex}}@media(min-width:768px){.container.extended{max-width:1024px;--left-sidebar-max-width:25%;--right-sidebar-max-width:30%}}@media(min-width:1024px){.container.extended{max-width:1280px;--left-sidebar-max-width:20%;--right-sidebar-max-width:30%}}@media(min-width:1280px){.container.extended{max-width:1536px;--left-sidebar-max-width:15%;--right-sidebar-max-width:25%}}@media(min-width:768px){.container.compact{--left-sidebar-max-width:25%;max-width:768px}}@media(min-width:1024px){.container.compact{max-width:1024px;--left-sidebar-max-width:20%}}@media(min-width:1280px){.container.compact{max-width:1280px}}.flex{display:flex;flex-direction:row}.flex.column{flex-direction:column}.flex.on-phone--column{flex-direction:column}@media(min-width:768px){.flex.on-phone--column{flex-direction:unset}}.flex .full-width{width:100%}main.main{order:-2;min-width:0;max-width:100%;flex-grow:1;display:flex;flex-direction:column;gap:var(--section-separation)}@media(min-width:768px){main.main{padding-top:var(--main-top-padding)}}.main-container{min-height:100vh;align-items:flex-start;padding:0 15px;gap:var(--section-separation);padding-top:var(--main-top-padding)}@media(min-width:768px){.main-container{padding:0 20px}}/*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}/*!* Hamburgers +* @description Tasty CSS-animated hamburgers +* @author Jonathan Suh @jonsuh +* @site https://jonsuh.com/hamburgers +* @link https://github.com/jonsuh/hamburgers*/.hamburger{padding-top:10px;display:inline-block;cursor:pointer;transition-property:opacity,filter;transition-duration:.15s;transition-timing-function:linear;font:inherit;color:inherit;text-transform:none;background-color:transparent;border:0;margin:0;overflow:visible}.hamburger:hover{opacity:.7}.hamburger.is-active:hover{opacity:.7}.hamburger.is-active .hamburger-inner,.hamburger.is-active .hamburger-inner::before,.hamburger.is-active .hamburger-inner::after{background-color:#000}.hamburger-box{width:30px;height:24px;display:inline-block;position:relative}.hamburger-inner{display:block;top:50%;margin-top:-2px}.hamburger-inner,.hamburger-inner::before,.hamburger-inner::after{width:30px;height:2px;background-color:var(--card-text-color-main);border-radius:4px;position:absolute;transition-property:transform;transition-duration:.15s;transition-timing-function:ease}.hamburger-inner::before,.hamburger-inner::after{content:"";display:block}.hamburger-inner::before{top:-10px}.hamburger-inner::after{bottom:-10px}.hamburger--spin .hamburger-inner{transition-duration:.22s;transition-timing-function:cubic-bezier(.55,.055,.675,.19)}.hamburger--spin .hamburger-inner::before{transition:top .1s .25s ease-in,opacity .1s ease-in}.hamburger--spin .hamburger-inner::after{transition:bottom .1s .25s ease-in,transform .22s cubic-bezier(.55,.055,.675,.19)}.hamburger--spin.is-active .hamburger-inner{transform:rotate(225deg);transition-delay:.12s;transition-timing-function:cubic-bezier(.215,.61,.355,1)}.hamburger--spin.is-active .hamburger-inner::before{top:0;opacity:0;transition:top .1s ease-out,opacity .1s .12s ease-out}.hamburger--spin.is-active .hamburger-inner::after{bottom:0;transform:rotate(-90deg);transition:bottom .1s ease-out,transform .22s .12s cubic-bezier(.215,.61,.355,1)}#toggle-menu{background:0 0;border:none;position:absolute;right:0;top:0;z-index:2;cursor:pointer;outline:none}[dir=rtl] #toggle-menu{left:0;right:auto}@media(min-width:768px){#toggle-menu{display:none}}#toggle-menu.is-active .hamburger-inner,#toggle-menu.is-active .hamburger-inner::before,#toggle-menu.is-active .hamburger-inner::after{background-color:var(--accent-color)}.menu{padding-left:0;list-style:none;flex-direction:column;overflow-y:auto;flex-grow:1;font-size:1.4rem;background-color:var(--card-background);box-shadow:var(--shadow-l1);display:none;margin:0 calc(var(--container-padding) * -1);padding:30px}@media(min-width:1280px){.menu{padding:15px 0}}.menu,.menu .menu-bottom-section{gap:30px}@media(min-width:1280px){.menu,.menu .menu-bottom-section{gap:25px}}.menu.show{display:flex}@media(min-width:768px){.menu{align-items:flex-end;display:flex;background-color:transparent;padding:0;box-shadow:none;margin:0}}.menu li{position:relative;vertical-align:middle;padding:0}@media(min-width:768px){.menu li{width:100%}}.menu li svg{stroke:currentColor;stroke-width:1.33;width:20px;height:20px}.menu li a{height:100%;display:inline-flex;align-items:center;color:var(--body-text-color);gap:var(--menu-icon-separation)}.menu li span{flex:1}.menu li.current a{color:var(--accent-color);font-weight:700}.menu .menu-bottom-section{margin-top:auto;display:flex;flex-direction:column;width:100%}.social-menu{list-style:none;padding:0;margin:0;display:flex;flex-direction:row;gap:10px}.social-menu svg{width:24px;height:24px;stroke:var(--body-text-color);stroke-width:1.33}.article-list{display:flex;flex-direction:column;gap:var(--section-separation)}.article-list article{display:flex;flex-direction:column;background-color:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--card-border-radius);overflow:hidden;transition:box-shadow .3s ease}.article-list article:hover{box-shadow:var(--shadow-l2)}.article-list article .article-image img{width:100%;height:150px;object-fit:cover}@media(min-width:768px){.article-list article .article-image img{height:200px}}@media(min-width:1280px){.article-list article .article-image img{height:250px}}.article-list article:nth-child(5n+1) .article-category a{background:#8ea885;color:#fff}.article-list article:nth-child(5n+2) .article-category a{background:#df7988;color:#fff}.article-list article:nth-child(5n+3) .article-category a{background:#0177b8;color:#fff}.article-list article:nth-child(5n+4) .article-category a{background:#ffb900;color:#fff}.article-list article:nth-child(5n+5) .article-category a{background:#6b69d6;color:#fff}.article-details{display:flex;flex-direction:column;justify-content:center;padding:var(--card-padding);gap:15px}.article-title{font-family:var(--article-font-family);font-weight:600;margin:0;color:var(--card-text-color-main);font-size:2.2rem}@media(min-width:1280px){.article-title{font-size:2.4rem}}.article-title a{color:var(--card-text-color-main)}.article-title a:hover{color:var(--card-text-color-main)}.article-subtitle{font-weight:400;color:var(--card-text-color-secondary);line-height:1.5;margin:0;font-size:1.75rem}@media(min-width:1280px){.article-subtitle{font-size:2rem}}.article-title-wrapper{display:flex;flex-direction:column;gap:8px}.article-time,.article-translations{display:flex;color:var(--card-text-color-tertiary);gap:15px}.article-time svg,.article-translations svg{vertical-align:middle;width:20px;height:20px;stroke-width:1.33;flex-shrink:0}.article-time time,.article-time a,.article-translations time,.article-translations a{font-size:1.4rem;color:var(--card-text-color-tertiary)}.article-time>div,.article-translations>div{display:inline-flex;align-items:center;gap:15px}.article-time{flex-wrap:wrap}.article-translations>div{flex-wrap:wrap}.article-category,.article-tags{display:flex;gap:10px}.article-category a,.article-tags a{color:var(--accent-color-text);background-color:var(--accent-color);padding:8px 16px;border-radius:var(--tag-border-radius);display:inline-block;font-size:1.4rem;transition:background-color .5s ease}.article-category a:hover,.article-tags a:hover{color:var(--accent-color-text);background-color:var(--accent-color-darker)}.article-list--compact{border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);background-color:var(--card-background);--image-size:50px}@media(min-width:768px){.article-list--compact{--image-size:60px}}.article-list--compact article>a{display:flex;align-items:center;padding:var(--small-card-padding);gap:15px}.article-list--compact article:not(:last-of-type){border-bottom:1.5px solid var(--card-separator-color)}.article-list--compact article .article-details{flex-grow:1;padding:0;min-height:var(--image-size);gap:10px}.article-list--compact article .article-title{margin:0;font-size:1.6rem}@media(min-width:768px){.article-list--compact article .article-title{font-size:1.8rem}}.article-list--compact article .article-image img{width:var(--image-size);height:var(--image-size);object-fit:cover}.article-list--compact article .article-time{font-size:1.4rem}.article-list--compact article .article-preview{font-size:1.4rem;color:var(--card-text-color-tertiary);margin-top:10px;line-height:1.5}.article-list--tile article{border-radius:var(--card-border-radius);overflow:hidden;position:relative;height:350px;width:250px;box-shadow:var(--shadow-l1);transition:box-shadow .3s ease;background-color:var(--card-background)}.article-list--tile article:hover{box-shadow:var(--shadow-l2)}.article-list--tile article.has-image .article-details{background-color:rgba(0,0,0,.25)}.article-list--tile article.has-image .article-title{color:#fff}.article-list--tile article .article-image{position:absolute;top:0;left:0;width:100%;height:100%}.article-list--tile article .article-image img{width:100%;height:100%;object-fit:cover}.article-list--tile article .article-details{border-radius:var(--card-border-radius);position:relative;height:100%;width:100%;display:flex;flex-direction:column;justify-content:flex-end;z-index:2;padding:15px}@media(min-width:640px){.article-list--tile article .article-details{padding:20px}}.article-list--tile article .article-title{font-size:2rem;font-weight:500;color:var(--card-text-color-main)}@media(min-width:640px){.article-list--tile article .article-title{font-size:2.2rem}}.widget{display:flex;flex-direction:column}.widget .widget-icon svg{width:32px;height:32px;stroke-width:1.6;color:var(--body-text-color)}.tagCloud .tagCloud-tags{display:flex;flex-wrap:wrap;gap:10px}.tagCloud .tagCloud-tags a{background:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--tag-border-radius);padding:8px 20px;color:var(--card-text-color-main);font-size:1.4rem;transition:box-shadow .3s ease}.tagCloud .tagCloud-tags a:hover{box-shadow:var(--shadow-l2)}.widget.archives .widget-archive--list{border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);background-color:var(--card-background)}.widget.archives .archives-year:not(:last-of-type){border-bottom:1.5px solid var(--card-separator-color)}.widget.archives .archives-year a{font-size:1.4rem;padding:18px 25px;display:flex}.widget.archives .archives-year a span.year{flex:1;color:var(--card-text-color-main);font-weight:700}.widget.archives .archives-year a span.count{color:var(--card-text-color-tertiary)}footer.site-footer{padding:20px 0 var(--section-separation);font-size:1.4rem;line-height:1.75}footer.site-footer:before{content:"";display:block;height:3px;width:50px;background:var(--body-text-color);margin-bottom:20px}footer.site-footer .copyright{color:var(--accent-color);font-weight:700;margin-bottom:5px}footer.site-footer .powerby{color:var(--body-text-color);font-weight:400;font-size:1.2rem}footer.site-footer .powerby a{color:var(--body-text-color)}.pagination{display:flex;background-color:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--card-border-radius);overflow:hidden;flex-wrap:wrap}.pagination .page-link{padding:16px 32px;display:inline-flex;color:var(--card-text-color-secondary)}.pagination .page-link.current{font-weight:700;background-color:var(--card-background-selected);color:var(--card-text-color-main)}@media(min-width:768px){.sidebar.sticky{position:sticky}}.left-sidebar{display:flex;flex-direction:column;flex-shrink:0;align-self:stretch;gap:var(--sidebar-element-separation);max-width:none;width:100%;position:relative;--sidebar-avatar-size:100px;--sidebar-element-separation:20px;--emoji-size:40px;--emoji-font-size:20px}@media(min-width:768px){.left-sidebar{width:auto;padding-top:var(--main-top-padding);padding-bottom:var(--main-top-padding);max-height:100vh}}@media(min-width:1536px){.left-sidebar{--sidebar-avatar-size:120px;--sidebar-element-separation:25px;--emoji-size:40px}}.left-sidebar.sticky{top:0}.left-sidebar.compact{--sidebar-avatar-size:80px;--emoji-size:30px;--emoji-font-size:15px}@media(min-width:1024px){.left-sidebar.compact header{flex-direction:row}}.left-sidebar.compact header .site-meta{gap:5px}.left-sidebar.compact header .site-name{font-size:1.4rem}@media(min-width:1536px){.left-sidebar.compact header .site-name{font-size:1.75rem}}.left-sidebar.compact header .site-description{font-size:1.4rem}.right-sidebar{width:100%;display:none;flex-direction:column;gap:var(--widget-separation)}.right-sidebar.sticky{top:0}@media(min-width:1024px){.right-sidebar{padding-top:var(--main-top-padding);padding-bottom:var(--main-top-padding)}}.sidebar header{z-index:1;transition:box-shadow .5s ease;display:flex;flex-direction:column;gap:var(--sidebar-element-separation)}@media(min-width:768px){.sidebar header{padding:0}}.sidebar header .site-avatar{position:relative;margin:0;width:var(--sidebar-avatar-size);height:var(--sidebar-avatar-size);flex-shrink:0}.sidebar header .site-avatar .site-logo{width:100%;height:100%;border-radius:100%;box-shadow:var(--shadow-l1)}.sidebar header .site-avatar .emoji{position:absolute;width:var(--emoji-size);height:var(--emoji-size);line-height:var(--emoji-size);border-radius:100%;bottom:0;right:0;text-align:center;font-size:var(--emoji-font-size);background-color:var(--card-background);box-shadow:var(--shadow-l2)}.sidebar header .site-meta{display:flex;flex-direction:column;gap:10px;justify-content:center}.sidebar header .site-name{color:var(--accent-color);margin:0;font-size:1.6rem}@media(min-width:1536px){.sidebar header .site-name{font-size:1.8rem}}.sidebar header .site-description{color:var(--body-text-color);font-weight:400;margin:0;font-size:1.4rem}@media(min-width:1536px){.sidebar header .site-description{font-size:1.6rem}}[data-scheme=dark] #dark-mode-toggle{color:var(--accent-color);font-weight:700}[data-scheme=dark] #dark-mode-toggle .icon-tabler-toggle-left{display:none}[data-scheme=dark] #dark-mode-toggle .icon-tabler-toggle-right{display:unset}#dark-mode-toggle{margin-top:auto;color:var(--body-text-color);display:flex;align-items:center;cursor:pointer;gap:var(--menu-icon-separation)}#dark-mode-toggle .icon-tabler-toggle-right{display:none}#i18n-switch{color:var(--body-text-color);display:inline-flex;align-content:center;gap:var(--menu-icon-separation)}#i18n-switch select{border:0;background-color:transparent;color:var(--body-text-color)}#i18n-switch select option{color:var(--card-text-color-main);background-color:var(--card-background)}html{font-size:62.5%;overflow-y:scroll}*{box-sizing:border-box}body{background:var(--body-background);margin:0;font-family:var(--base-font-family);font-size:1.6rem;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*{scrollbar-width:auto;scrollbar-color:var(--scrollbar-thumb)transparent}::-webkit-scrollbar{height:auto}::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb)}::-webkit-scrollbar-track{background-color:transparent}.article-page.hide-sidebar-sm .left-sidebar{display:none}@media(min-width:768px){.article-page.hide-sidebar-sm .left-sidebar{display:inherit}}.article-page .main-article{background:var(--card-background);border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);overflow:hidden}.article-page .main-article .article-header .article-image img{height:auto;width:100%;max-height:50vh;object-fit:cover}.article-page .main-article .article-header .article-details{padding:var(--card-padding);padding-bottom:0}.article-page .main-article .article-content{margin:var(--card-padding)0;color:var(--card-text-color-main)}.article-page .main-article .article-content .footnotes{font-family:var(--base-font-family)}.article-page .main-article .article-content img{max-width:100%;height:auto}.article-page .main-article .article-footer{margin:var(--card-padding);margin-top:0}.article-page .main-article .article-footer section:not(:first-child){margin-top:var(--card-padding)}.article-page .main-article .article-footer section{color:var(--card-text-color-tertiary);text-transform:uppercase;display:flex;align-items:center;font-size:1.4rem;gap:15px}.article-page .main-article .article-footer section svg{width:20px;height:20px;stroke-width:1.33}.article-page .main-article .article-footer .article-tags{flex-wrap:wrap;text-transform:unset}.article-page .main-article .article-footer .article-copyright a,.article-page .main-article .article-footer .article-lastmod a{color:var(--body-text-color)}.article-page .main-article .article-footer .article-copyright a.link,.article-page .main-article .article-footer .article-lastmod a.link{box-shadow:unset}.widget--toc{background-color:var(--card-background);border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);display:flex;flex-direction:column;color:var(--card-text-color-main);overflow:hidden}.widget--toc ::-webkit-scrollbar-thumb{background-color:var(--card-separator-color)}.widget--toc #TableOfContents{overflow-x:auto;max-height:75vh}.widget--toc #TableOfContents ol,.widget--toc #TableOfContents ul{margin:0;padding:0}.widget--toc #TableOfContents ol{list-style-type:none;counter-reset:item}.widget--toc #TableOfContents ol li a:first-of-type::before{counter-increment:item;content:counters(item,".")". ";font-weight:700;margin-right:5px}.widget--toc #TableOfContents>ul{padding:0 1em}.widget--toc #TableOfContents li{margin:15px 0 15px 20px;padding:5px}.widget--toc #TableOfContents li>ol,.widget--toc #TableOfContents li>ul{margin-top:10px;padding-left:10px;margin-bottom:-5px}.widget--toc #TableOfContents li>ol>li:last-child,.widget--toc #TableOfContents li>ul>li:last-child{margin-bottom:0}.widget--toc #TableOfContents li.active-class>a{border-left:var(--heading-border-size)solid var(--accent-color);font-weight:700}.widget--toc #TableOfContents ul li.active-class>a{display:block}.widget--toc #TableOfContents>ul>li.active-class>a{margin-left:calc(-25px - 1em);padding-left:calc(25px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li.active-class>a{margin-left:calc(-9px - 1em);padding-left:calc(9px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li.active-class>a{margin-left:calc(-60px - 1em);padding-left:calc(60px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li.active-class>a{margin-left:calc(-44px - 1em);padding-left:calc(44px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li>ul>li.active-class>a{margin-left:calc(-95px - 1em);padding-left:calc(95px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li>ol>li.active-class>a{margin-left:calc(-79px - 1em);padding-left:calc(79px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li>ul>li>ul>li.active-class>a{margin-left:calc(-130px - 1em);padding-left:calc(130px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li>ol>li>ol>li.active-class>a{margin-left:calc(-114px - 1em);padding-left:calc(114px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li>ul>li>ul>li>ul>li.active-class>a{margin-left:calc(-165px - 1em);padding-left:calc(165px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li>ol>li>ol>li>ol>li.active-class>a{margin-left:calc(-149px - 1em);padding-left:calc(149px + 1em - var(--heading-border-size));display:block}.related-content{overflow-x:auto;padding-bottom:15px}.related-content>.flex{float:left}.related-content article{margin-right:15px;flex-shrink:0;overflow:hidden;width:250px;height:150px}.related-content article .article-title{font-size:1.8rem;margin:0}.related-content article.has-image .article-details{padding:20px;background:linear-gradient(0deg,rgba(0,0,0,.25) 0%,rgba(0,0,0,.75) 100%)}.article-content{font-family:var(--article-font-family);font-size:var(--article-font-size);padding:0 var(--card-padding);line-height:var(--article-line-height)}.article-content>p{margin:1.5em 0}.article-content h1,.article-content h2,.article-content h3,.article-content h4,.article-content h5,.article-content h6{margin-inline-start:calc((var(--card-padding)) * -1);padding-inline-start:calc(var(--card-padding) - var(--heading-border-size));border-inline-start:var(--heading-border-size)solid var(--accent-color)}.article-content figure{text-align:center}.article-content figure figcaption{font-size:1.4rem;color:var(--card-text-color-secondary)}.article-content blockquote{position:relative;margin:1.5em 0;border-inline-start:var(--blockquote-border-size)solid var(--card-separator-color);padding:15px calc(var(--card-padding) - var(--blockquote-border-size));background-color:var(--blockquote-background-color)}.article-content blockquote .cite{display:block;text-align:right;font-size:.75em}.article-content blockquote .cite a{text-decoration:underline}.article-content hr{width:100px;margin:40px auto;background:var(--card-text-color-tertiary);height:2px;border:0;opacity:.55}.article-content code{color:var(--code-text-color);background-color:var(--code-background-color);padding:2px 4px;border-radius:var(--tag-border-radius);font-family:var(--code-font-family)}.article-content a,.article-content code{word-break:break-word}.article-content .gallery{position:relative;display:flex;flex-direction:row;justify-content:center;margin:1.5em 0;gap:10px}.article-content .gallery figure{margin:0}.article-content pre{overflow-x:auto;display:block;background-color:var(--pre-background-color);color:var(--pre-text-color);font-family:var(--code-font-family);line-height:1.428571429;word-break:break-all;padding:var(--card-padding)}[dir=rtl] .article-content pre{direction:ltr}.article-content pre code{color:unset;border:none;background:0 0;padding:0}.article-content .highlight{background-color:var(--pre-background-color);padding:var(--card-padding);position:relative}.article-content .highlight:hover .copyCodeButton{opacity:1}[dir=rtl] .article-content .highlight{direction:ltr}.article-content .highlight pre{margin:initial;padding:0;margin:0;width:auto}.article-content .copyCodeButton{position:absolute;top:calc(var(--card-padding));right:calc(var(--card-padding));background:var(--card-background);border:none;box-shadow:var(--shadow-l2);border-radius:var(--tag-border-radius);padding:8px 16px;color:var(--card-text-color-main);cursor:pointer;font-size:14px;opacity:0;transition:opacity .3s ease}.article-content .table-wrapper{padding:0 var(--card-padding);overflow-x:auto;display:block}.article-content table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:1.5em;font-size:.96em}.article-content th,.article-content td{text-align:left;padding:4px 8px 4px 10px;border:1px solid var(--table-border-color)}.article-content td{vertical-align:top}.article-content tr:nth-child(even){background-color:var(--tr-even-background-color)}.article-content .twitter-tweet{color:var(--card-text-color-main)}.article-content .video-wrapper{position:relative;width:100%;height:0;padding-bottom:56.25%;overflow:hidden}.article-content .video-wrapper>iframe,.article-content .video-wrapper>video{position:absolute;width:100%;height:100%;left:0;top:0;border:0}.article-content .gitlab-embed-snippets{margin:0!important}.article-content .gitlab-embed-snippets .file-holder.snippet-file-content{margin-block-end:0!important;margin-block-start:0!important;margin-left:calc((var(--card-padding)) * -1)!important;margin-right:calc((var(--card-padding)) * -1)!important;padding:0 var(--card-padding)!important}.article-content blockquote,.article-content figure,.article-content .highlight,.article-content pre,.article-content .gallery,.article-content .video-wrapper,.article-content .table-wrapper,.article-content .s_video_simple{margin-left:calc((var(--card-padding)) * -1);margin-right:calc((var(--card-padding)) * -1);width:calc(100% + var(--card-padding) * 2)}.article-content .katex-display>.katex{overflow-x:auto;overflow-y:hidden}.article-content kbd{border:1px solid var(--kbd-border-color);font-weight:700;font-size:.9em;line-height:1;padding:2px 4px;border-radius:4px;display:inline-block}.section-card{border-radius:var(--card-border-radius);background-color:var(--card-background);padding:var(--small-card-padding);box-shadow:var(--shadow-l1);display:flex;align-items:center;gap:20px;--separation:15px}.section-card .section-term{font-size:2.2rem;margin:0;color:var(--card-text-color-main)}.section-card .section-description{font-weight:400;color:var(--card-text-color-secondary);font-size:1.6rem;margin:0}.section-card .section-details{flex-grow:1;display:flex;flex-direction:column;gap:8px}.section-card .section-image img{width:60px;height:60px}.section-card .section-count{color:var(--card-text-color-tertiary);font-size:1.4rem;margin:0;font-weight:700;text-transform:uppercase}.subsection-list{overflow-x:auto}.subsection-list .article-list--tile{display:flex;padding-bottom:15px}.subsection-list .article-list--tile article{width:250px;height:150px;margin-right:20px;flex-shrink:0}.subsection-list .article-list--tile article .article-title{margin:0;font-size:1.8rem}.subsection-list .article-list--tile article .article-details{padding:20px}.not-found-card{background-color:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--card-border-radius);padding:var(--card-padding)}.search-form{position:relative;--button-size:80px}.search-form.widget{--button-size:60px}.search-form.widget label{font-size:1.3rem;top:10px}.search-form.widget input{font-size:1.5rem;padding:30px 20px 15px}.search-form p{position:relative;margin:0}.search-form label{position:absolute;top:15px;inset-inline-start:20px;font-size:1.4rem;color:var(--card-text-color-tertiary)}.search-form input{padding:40px 20px 20px;border-radius:var(--card-border-radius);background-color:var(--card-background);box-shadow:var(--shadow-l1);color:var(--card-text-color-main);width:100%;border:0;-webkit-appearance:none;transition:box-shadow .3s ease;font-size:1.8rem}.search-form input:focus{outline:0;box-shadow:var(--shadow-l2)}.search-form button{position:absolute;inset-inline-end:0;top:0;height:100%;width:var(--button-size);cursor:pointer;background-color:transparent;border:0;padding:0 10px}.search-form button:focus{outline:0}.search-form button:focus svg{stroke-width:2;color:var(--accent-color)}.search-form button svg{color:var(--card-text-color-secondary);stroke-width:1.33;transition:all .3s ease;width:20px;height:20px}a{text-decoration:none;color:var(--accent-color)}a:hover{color:var(--accent-color-darker)}a.link{box-shadow:0 -2px rgba(var(--link-background-color),var(--link-background-opacity))inset;transition:all .3s ease}a.link:hover{box-shadow:0 calc(-1rem * var(--article-line-height))rgba(var(--link-background-color),var(--link-background-opacity-hover))inset}.section-title{text-transform:uppercase;margin-top:0;margin-bottom:10px;display:block;font-size:1.6rem;font-weight:700;color:var(--body-text-color)}.section-title a{color:var(--body-text-color)} \ No newline at end of file diff --git a/search/index.html b/search/index.html new file mode 100644 index 0000000..6413865 --- /dev/null +++ b/search/index.html @@ -0,0 +1,362 @@ + + + + +Search + + + + + + + + + + + + + + + +
+ +
+
+

+ + +

+ + +
+ +
+

+
+
+ + + + + + +
+
+ + + + + diff --git a/search/index.json b/search/index.json new file mode 100644 index 0000000..c712c54 --- /dev/null +++ b/search/index.json @@ -0,0 +1 @@ +[{"content":"前言 裸辞后,我花了一个多月开发我的第一个独立开发产品RedisMate。其MVP版本上线五天,产品从没有排名,到冲到了国区mac app store开发工具类排名榜的前十,并持续有了几笔收入。\n那么,我是怎么做的呢?\n我是一个正在尝试独立开发的普通程序员,在此跟各位分享我的过程,不仅是我对自己的复盘,还希望能够给一些正在或准备尝试独立开发的兄弟一点参考。所有的过程都是按照我主观的想法去推进执行的,如果有更好的建议,欢迎交流!\n本次分享分为上下两篇文章,分别为产品设计和产品推广两个主题。\n起源 首先,我并不是因为我有一个很棒的点子或产品,而选择去裸辞,去独立开发。\n而是我因为别的原因选择了裸辞,然后开始把独立开发作为未来的一条路,并尝试去走。\n所以,也可以说,一开始我是为了独立开发而去独立开发。\n产品选择 既然我没有一个很棒的点子去完成一个产品,那么我就开始去找。\n自身条件 首先,为了能够独立开发,我要审视自身,我会什么?\n罗列下我的开发技能:\n前端:Vue、TS、\u0026hellip; 后端:Go、Python、Rust 数据库:MySQL、Redis、PostgreSQL、ClickHouse、\u0026hellip; 运维:Docker、MQ、Nginx、\u0026hellip; 客户端/原生:Electron、Flutter、SwiftUI 硬件:esp32 检查下其它独立开发必需技能:\n产品:自嗨水平 设计:自嗨水平 运营:几乎没有 罗列下我熟悉的可以寻找需求的领域:\n开发 玩游戏 Linux日常办公 Mac日常办公 我能在独立开发过程中投入多少资源:\n时间:全职 资金:起初的时候几乎完全不想投入一块钱,但起码要支付苹果个人开发者年费+服务器年费+域名年费 产品形态 那么,基于开发技能,得出我可以做的产品的形态:\n安卓app ios app Windows/Linux/macOS应用程序 SaaS 硬件 浏览器插件 编辑器插件 微信小程序 但是考虑到希望以较小的成本来尝试独立开发,和希望能够短期得到收入来验证产品,那么就剩下了\n安卓app ios app Windows/Linux/macOS应用程序 并且还都是离线应用,不需要服务器的支撑,没有太多服务器的额外成本,服务器仅仅需要最小配置用来备案即可。\n考虑到付费方式的成本、上架的成本,再排除掉安卓app、Windows/Linux应用程序。\n那么就只剩下\nios app macOS应用程序 产品需求 确定了产品形态,我就要考虑,到底要做什么产品呢?产品需求是什么?\n如果我从当下很火但我完全不熟悉的领域去找需求,那我肯定没法挖掘真正的需求。\n如果我找一个熟悉某个领域的人咨询,他并没有挖掘需求的能力,无法识别出真正的需求,这一点在我多年的工作中已经见识过太多太多次了,最后完完全全就是一个定制化项目,在市场上根本没有竞争力。\n这就要从上述罗列的,我熟悉的领域去找。\n所以我把目标放在了“开发”上。“开发”是我最熟悉的领域,在其中这么多年了,需求来源于我自己,我就是用户,我就可以直接验证这个需求是不是伪需求,这个功能是不是好功能。\n并且,鉴于我三年前开发过一款开源的Redis GUI Client——RedisFish,积累了一些经验,并且后来因为工作搁置了,导致一直没有完成也心有遗憾。\n所以最终决定开发一款Redis GUI for Mac。\n市场调研 在决定产品之后,我先去问了一圈我的朋友和前同事,他们正在用的Redis GUI是什么。\n得到的回复是如下产品:\nRedisDesktopManager AnotherRedisDesktopManager Medis2 RedisFish(被我安利用我自己的开源产品,哈哈\u0026hellip;) 其中RedisDesktopManager和AnotherRedisDesktopManager是最多的,毕竟是老牌的强劲产品!\n然后,我又去翻了些论坛和帖子,发现有些人在发帖问mac上有哪些推荐的Redis GUI,但是哪怕是老外,也是如下选项居多:\nRedisDesktopManager AnotherRedisDesktopManager Medis2 RedisInsight 接着,我也去了解了下近期新出的,比如Tiny RDM,听劝作品,真的很棒的,哈哈。\n然后还有几个出现在比如gitee等平台的。\n所以目前mac原生的也只有Medis2。\n最后,我就要说那个经典台词了:没有能够满足我需求的!(不是\u0026hellip;\n重新构思MVP版本 到这个事件前,我已经开发完了既定的MVP版本,为v 1.1,此时的RedisMate是一个完成了基础Redis功能的App。\n而我也已经怀着激动而又紧张的心情,在想如何推广MVP版本了。\n但是,在我准备尝试推广的那个早上,在上厕所的时候,当时正在看《人性的弱点》。刚好看到书上讲了一个推销员的案例,推销员疯狂地讲述自己的产品怎么怎么好,让作者要赶紧买。\n因此作者就在书上写道:\n人们其实什么东西都不需要。如果我们想买点什么,早就出门去买回来了。人们真正需要的,是解决问题的方式。人类永远都面临着种种问题,永远都需要这些问题的解决方案——如果销售人员能够证明其服务或产品可以帮助人们解决问题,不用推销,我们就会主动掏钱。对消费者而言,“主动买”比“被推销”的感觉好得多。\n所以,我就在想,我现在做的这是什么啊?!我能给其他人解决什么问题呢?凭什么要选择我的,而不是别的成熟产品?\n我就开始重新构思MVP版本,希望能够加上独特的、创新的功能,能够真实解决用户问题的功能。\n创新 寻找创新 如何发现问题并解决问题?可以从经济学的四种不同类型的效用来入手:\n场所效益:使得原本无法接触的事物变得可接触 形式效用:通过重新排列现有部件使某物更有价值 时间工具:让事情变快的工具 拥有权利:去除中间人 在此处,我的产品RedisMate上,最简单也是最快的的创新方式就是“让事情变得更快”。\n我先明确Redis GUI使用频率最高的功能,然后从那个功能入手,让用户能够立竿见影感受到RedisMate的便捷。\n那么,经过自己的总结,和跟朋友的沟通,我总结出,Redis GUI工具,使用频率从高到低的功能分别是:查(甚至是搜索)、删、改、增。\n因此,我就从搜索功能入手。\n我把RedisMate搜索功能的所有步骤罗列出来,然后思考,有哪些步骤可以减去?如何减少步骤?\n最终就得到了现在“快速搜索”功能!\n当Redis GUI窗口常驻时,一般最少需要约7步操作,那么在RedisMate上最少只要3步。\n当Redis GUI仅打开没有连接任何服务器时,一般最少需要约9步操作,但是在RedisMate上最少只要3步。\n当Redis GUI反复搜索固定key时,一般最少需要约4步,但是在RedisMate最少仅仅需要1步。\n所以,我在RedisMate上针对搜索场景,进行了优化,并确实在推广时,得到了不少积极反馈。\n并且,基于上述过程,优化了RedisMate的布局和一些细微的功能,让用户能够更直观更快去操作。\n挖掘需求 等我后面回过神的时候,才发现我一直有这个需求。\n经常性,我要前后端一起开发,由于是微服务,多个节点并发开发和调试,每次都要开超多窗口。\n我经常要切换窗口时,要愣一会儿思考一下,我要切什么窗口来着\u0026hellip;\n所以我就一直在想,能不能省略掉这些窗口,窗口自己呼出然后切回去!\n现在RedisMate就可以了,这就解决了我曾经的问题。\n然后,我就又想到曾经的一个抱怨,有时候在调试时,代码有点问题,或者业务链路较长,需要联调时间有点长,或者MQ驱动的业务节点产生key有点慢等等,key在我查的时候没有出现。我就要反复调出Redis GUI然后点点点后搜索再点点点,发现不对!调一下代码,再来一遍!\n我当时就在想,要是GUI能帮我盯着就好了。\n所以,我设计了\u0026quot;Search Until Found\u0026quot;功能,在得到朋友的肯定后,将其加入到了MVP版本中 。\n总结 首先,我很庆幸自己这些年学习和玩了不少技术,让我能够在想去独立开发的时候,不仅能够独立完成开发,还能有多种产品形态可以选择。并且我也有意识地去读技术之外的书,让我在其它领域有那么点点基础,只可惜没有学得更多。\n其次,我觉得对我而言,苹果生态开发是当前成本最小的方式。\n然后,我从我熟悉的领域入手,寻找自己的痛点,验证自己的需求,得到解决方案后,向身边的目标用户寻求验证。\n最后,花一个多月开发完MVP版本,推出来验证自己的产品。\n最后 感谢你看完这篇文章,希望我的经过能够给你一些参考,帮助到你。\n本次分享的下一篇文章——产品推广方面,我正在努力写,敬请期待并关注我以获取更新。\n如果你对我的产品RedisMate感兴趣,非常欢迎点击此处了解和下载,或者在mac app store中搜索“RedisMate“。\n最后,要感谢各位道上兄弟的关照,给了很多支持和反馈。\n还要特别感谢那些购买高级版的兄弟,非常感谢你们的支持!\n","date":"2024-01-24T23:53:17Z","image":"https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/cover_hu0d3485c6faa3310f32bd6f2107f7369b_794829_120x120_fill_box_smart1_3.png","permalink":"https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/","title":"裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上)"},{"content":"前言 设想一个场景:\n你作为一个程序员,听着攒劲的小曲,写着优雅的代码,加班加点终于写完了迭代中分配的模块需求。然后你跟别的同事一联调,却发现各种问题。\n你跟同事A说:“你得在这种情况下给我数据A。“\n同事A一脸懵逼:“这种情况需求也没写啊,在这种情况下我也没有数据A啊!“\n然后你和同事A一起顺着业务流程,找到了负责上层模块的同事B。\n同事B一脸懵逼:“这个迭代中我没有这个模块的开发需求啊,这种情况下我也没有数据A啊!”\n然后你和同事A、同事B一起顺着业务流程,找到了负责上层模块的\u0026hellip;\u0026hellip;\n最后,你和同事ABC\u0026hellip;一起找到了产品,产品一脸懵逼:“那没办法,就改需求吧\u0026hellip;\u0026hellip;”\n你眼看着所剩无几的迭代时间,生无可恋地掏出手机,发个微信取消了这周末的相亲\u0026hellip;\n不知道程序员xdm有没有经历过这样的场景,如果各位程序员xd没有经历过这样的场景,那真的是恭喜你了,且行且珍惜!从毕业后参加工作开始,我是真的经历太多了,太痛了!\n我之后不断学习各种方法,尝试去破解这样的困局。虽然说,工作中的问题很多,但是能解决一个是一个,后面再慢慢跟各位分享其他问题中我的解决方案。\n所以,本篇文章将分享一波关于这类问题我的解决方案。谨代表个人主观观点。\n痛点 需求是用户的痛点,但是在这个场景下,是程序员的痛点\u0026hellip;\n那么,仔细分析一下,这个场景下的痛点,到底是什么呢?我认为是如下两点:\n产品给到的需求无法自洽,且场景覆盖不够 开发团队的技术评审和技术文档不到位 开发各自按照模块开发,忽略外部环境,仅仅当各自开发完成后联调时候才会组装应用,发现问题 当然,可能有程序员xd会疑问:这种问题不是靠严谨的开发流程就可以避免吗?\n理论上来说是这样的,但是以我个人经历而言,不管是大团队还是小团队,总有很多不稳定的因素,比如说迭代周期、协作流程的规范性等等。\n特别是在创业团队,或者有些项目特别着急的时候,经常是没有足够的时间给到团队的各个角色去充分准备的,甚至为了赶时间,会精简一些环节甚至直接去掉。这种时候出现这种问题导致改需求,往往是非常可怕的,后果也往往是开发团队加班。\n解决方案 我目前发现且实践后效果较好的方案,就是“曳光弹式开发”。\n什么是曳光弹 经常关注军事的小伙伴儿,应该了解,曳光弹是一种运用于军事场景的特殊子弹。\n我们都知道武器发射时需要瞄准,比如枪就通过照门和准星来瞄准,而坦克和装甲车以及飞机都有专用的瞄准具,非常复杂。对于轻武器来说,一般攻击距离都比较近,使用自带的机械瞄准具或者外加的光学瞄准镜都足够,但是在距离稍远的时候瞄准镜显然不够。而对于飞机和战斗机来讲,在空中飞行时飞行姿态变化多样,有时候进攻的时间很短,需要快速射击并且调整弹道,这时候就需要可以发光的曳光弹。\n曳光弹正如其名,可以发光而且指示弹道。曳光弹的结构比一般子弹更加复杂。子弹的弹壳部分和一般子弹一样,前半部分是钢心或者铅心的弹头,但是在后部有一个空腔,一般称作曳光管,里面填充着曳光剂。曳光剂的成分还比较复杂,一般来说主要成分是镁粉和铝镁合金粉,用来燃烧,除此之外还有硝酸锶。这样一来,燃烧的时候硝酸锶就会发出红光。大家平时看到的很多曳光弹还有黄光和绿光,加入钠盐就会发出黄光,而加入铜盐就会发出绿光。除此之外还在表面加入一层过氧化钡,以保证曳光剂被点燃。\n对于机枪手来说,如果射击距离较远,自然不能选择过于精确的设计方式,也就是说不能靠瞄准具来射击。而一般的机枪主要起压制和面杀伤作用,加入曳光弹就是机枪手更加快速方便的控制弹道,随时变化攻击的方向。如果没有曳光弹的话,射手根本无法发现自己的子弹弹道,也就很难去调整弹道。毕竟枪械射击温度升高之后,弹道会有所变化,不同的弹药不同的枪管也都会改变子弹的弹道,如果仅仅依靠枪械自身的瞄准具去调整反而会适得其反,而曳光弹就很好的解决了这个问题。\n在开发中的作用 理解曳光弹本身在军事中的作用后,我们再来看曳光弹式开发如何在开发中起作用。\n开发团队在接手到产品给到的业务需求后,特别是构建一些以前从未做过的东西时,对这个产品/功能的最终成效是模糊的。\n程序员就像坦克上的机枪手一样,在尝试在黑暗中击中目标。但是如果像文章一开始的时候,大家先埋头写自己的模块,然后再联调,最终发现问题,感觉就像一上战场,大家都朝着各自理解的方向拼命清空弹夹,击中目标的寥寥无几,最后受伤的还是自己。\n所以,此时,就需要先使用几颗曳光弹,去击中目标,在黑暗中划出轨迹,开发团队再调整方向,对着目标集中火力。\n如何应用 非常简单!步骤如下:\n找出级别最高的需求 以完成最基础的完整功能为目标,从前端到部署,开发一个可以运行的骨架 最后上相关需求的测试案例 这样,射出一颗曳光弹,穿透客户端、后端、数据库、运维、测试等不同层面。一旦击中目标——即符合用户需求,后续的任务便大都是搬砖的活儿,去丰富这个骨架。\n曳光弹并不是总能击中目标的,中途若是发现未能击中目标,便可在前期以极小成本去调整曳光弹的方向,继续发射曳光弹。\n如果你要问,什么是“最基础的完整功能”,那么可以这么说,那么举个例子:前端不要任何样式,直接能够满足比如表单功能即可,后端不用任何校验,能够处理和传递数据即可。\n总结 为了解决文章开头的问题,需要使用曳光弹式开发,先开发一个骨架,确认满足需求后,即可继续丰富骨架,完成产品。\n作为团队的一员,团队的每一个角色都很重要,程序员跟产品也是需要紧密协作,互帮互助,才能使得产品更好,也使得业绩更好。\n","date":"2023-12-05T15:56:29Z","image":"https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/cover_hud87aacab5fb26efd5717fb6564f41dda_2230004_120x120_fill_box_smart1_3.png","permalink":"https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/","title":"程序员如何快速验证业务需求?"},{"content":"一. 前言 最近很多小伙伴儿咨询我关于学习技术方面的问题,我都一一回复了。这里也专门出一个总结,关于我个人这些年入门新的编程语言和框架的经验和技巧,来跟大家分享一下,希望能够所有帮助。\n本次分享分为两个时期:\n有编程经验时期 新手时期 二. 有编程经验时期 当我有了一定开发经验后,去学习一个新的编程语言或者框架,我会注意如下两点:\n1. 寻找共性 编程语言 所有的编程语言或者框架,都不是凭空产生的。当前比较流行的语言,大都是类C语言,比如我们常见的Java、Python、JavaScript、Golang等等。\n所以,我们学习的编程语言,除了特有的特性,大都是相似的,也就是它们的共性。我们只要理解和掌握这些类C语言的共性,就可以很快地掌握一门新的编程语言。\n那么,哪些是共性呢?比如说变量、常量、运算符、判断、循环、函数、面向对象等等。\n我们在学习一门新的编程语言的时候,是不是必然要学习如上这些共性的知识点?\n那么,当你已经有一个编程语言的基础后,当你在学习和写代码的时候,这些共性可以直接使用你已学会的编程语言的知识去代入。\n举个例子,比如我已经会Go了(不过,当你先学Go的话,可能不太知道面向对象是什么/狗头),要去学一下Python。\n我需要做的就是代入:\nGo语言声明变量是a := 1,考虑到Python没有强类型,那么就直接a = 1就完事了。\nGo语言声明函数是func aFunction(username String) {},那么Python不过是换了个关键词,去掉了强类型,变成def aFunctino(username):,当然Python如果要申明类型也是可以的,写成def login(username: str):。\n其中,比如运算符大都是一样的,判断也是类似。\n但是突然你发现,怎么Python的循环不一样,然后发现Python的循环好方便,但是其实原理还是和Go一样的。甚至,当你遇到不知道怎么处理的时候,直接来一波for (i := 0; i \u0026lt; count; i++)。当然,Python没法这么写。\n除了我举的这些例子,还有比如正则、数据类型、数据结构等等,都可以作为共性来代入。\n框架 其实,框架也是如此。此处以web框架为例,比如Python的Flask、Django、FastApi,Go的Gin,Node的express,Java的SpringBoot等,都是比较熟知的框架。\n这些Web框架,都是依托于网络的,大都是http(s)请求,甚至其他TCP请求。我们可以只在网络层面来看,它们共性就是request和response。所以在网络层面就可以理解为,后端web框架就是一个接收request和response的东西。\n那么,看具体框架的功能上,可以抽象出路由、中间件。\n所以,对于一个web框架,其共性为request、response、路由、中间件。\n因此,我们不管是使用Flask、Gin还是SpringBootd等,可以使用我们已有的框架知识去代入:\n如何接收request,比如获取request body、headers等 如何使用中间件处理,比如使用中间件鉴权、传递上下文等 如何配置路由,让接口对外暴露 如何返回response,比如返回http status code、json等 那么,就可以完成一个新的web框架的入门了。\n其它 除了编程语言和框架,其实还有很多别的都是拥有共性,可以快速入门的,比如Nginx和Apache,比如Linux发行版本,比如Kong和ApiSix,比如不同的数据库等等。甚至最近我发现,wasm并不是前端特有的,EBPF也在用wasm。\n2. 看官方文档 编程语言的情况还比较少,更多是学习框架时,一定要看官方文档。\n网上虽然有很多教程,但是毕竟是二次加工的,可能存在信息遗漏、版本更新等等问题。\n官方文档可能比较生涩,但是它是最全最新的一手文档,会非常有助于少走弯路。\n再次强调,一定要看官方文档!\n二. 新手时期 当我还是新手的时候,我其实也常常苦恼于入门——教程看了好多遍,跟着教程敲了一遍又一遍,感觉还是只会基础,自己想写点什么却不会。\n后来,当我入门一段时间后,我返回去想一想,才想明白一些技巧。\n所以,以我的经历,总结如下步骤:\n1. 仅学基础 一开始的时候,我也是不停地啃教程,不管是文档还是视频,但是发现我还是只会基础理论知识,那些高级特性案例都敲了,却并不明白什么意思。\n后来,我就发现,对于我而言,并不需要学太多,只要学会基础即可,甚至基础也不用太深入。\n2. 自己构思写一个项目 跟着教程敲代码,效果甚微。\n要自己构思一个项目,比如写一个TODO,写一个命令行聊天工具,写一个博客等等简单的程序。最好是自己感兴趣的领域。\n但是,需要注意的是,一定要自己去构思如何写:\n你准备写一个什么程序?要达到什么效果? 写这个程序有哪些功能? 这些功能都应该怎么实现? 然后,直接开始写代码!哪怕只是创建了几个文件。\n只有自己思考的,才能深刻理解和记忆。\n3. 不会就去学,不懂就复制过来用 当然,因为仅仅学了基础,你会发现很多功能并不会,甚至比如如何读取一个文件都不会。\n如果这时候,遇到不会的,就当场去学,哪怕是高级特性,学到把不会的这个点能做出来为止。\n如果一个功能不懂什么意思,搞不明白,那么直接复制过来,调试到能满足功能为止,然后将这段代码和这个场景记住。\n4. 多写代码,迈过门槛 我一直觉得写代码有一个门槛,在迈过门槛前,学习会非常困难,但是,一旦迈过这个门槛,你就会发现,学起来异常轻松,你所学的技术可以(相对)融会贯通。\n那么迈过这个门槛的方法就是:重复2和3两个步骤,多写代码,多思考。\n是的,不断重复,然后可能某一天,当你对你学的技术恍然大悟的时候,那么其实就是迈过了那个门槛。\n然后,之后便是更深入地学习了。\n三. 总结 一路走来,我觉得最重要的是“多写代码,多思考”。不少代码写到最后甚至都是肌肉记忆了,但是代码还是会变的,自己思考的方式和过程,自己沉淀的知识才是最宝贵的,足以支撑我能够不断学习新的技术,适应新的模式。\n","date":"2023-11-29T21:10:40Z","image":"https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/cover_hu289f93b8ec41ea81055c11038b70f240_213994_120x120_fill_q75_box_smart1.jpg","permalink":"https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/","title":"如何快速入门新的编程语言和框架?"},{"content":"前言 在上一篇文章《使用Rust和WebAssembly整花活儿(三)——Rust与JS交互》中,讲述了Rust与JS的交互,包括Rust与JS的函数相互调用,比较炸裂的功能就是使用JS调用Rust的struct,JS本身连struct都没有,居然可以调用Rust的struct,这对于Rust开发者的开发体验而言,是真的很棒!\n基于前面的系列文章,已经足以使用Rust开发一个完整的功能了。\n但是,在前端引入wasm文件时,还是可能存在一些问题,比如wasm文件较大,导致网页访问时间较长,用户体验较差。本篇文章将会通过多种途径来减少Rust编译wasm文件的体积,以减少前端加载wasm文件的时间。\n曾经一段时间,我一直用Go开发WebAssembly,其编译后的wasm文件体积还是较大的,为了减少wasm文件的体积真是煞费苦心,1xMB大小的wasm文件真的是太痛了\u0026hellip;\u0026hellip;但是体积优化往往都会指向一条“充满魅惑的不归路”——换成Rust开发😆!如果你已经在用Rust开发WebAssembly了,那么恭喜你,对Go而言,在wasm体积上,你已经赢在起跑线上了。\n环境 Rust 1.70.0 wasm-bindgen 0.2.87 查看体积 在真正去减少体积前,我们需要来先看一下,当前情况下体积是多少,方便后续对比前后体积。\n查看体积的方式有多种,这里推荐几个,(Linux和MacOS)使用其一即可。\nls 可以使用ls -l或者ll:\n1 2 $ ll pkg/hello_wasm_bg.wasm -rw-r--r-- 1 kuari staff 23K Jul 20 21:52 pkg/hello_wasm_bg.wasm stat 1 2 $ stat pkg/hello_wasm_bg.wasm 16777222 142141572 -rw-r--r-- 1 kuari staff 0 23347 \u0026#34;Jul 20 21:52:53 2023\u0026#34; \u0026#34;Jul 20 21:52:01 2023\u0026#34; \u0026#34;Jul 20 21:52:01 2023\u0026#34; \u0026#34;Jul 20 21:52:01 2023\u0026#34; 4096 48 0 pkg/hello_wasm_bg.wasm wc 1 2 $ wc -c pkg/hello_wasm_bg.wasm 23347 pkg/hello_wasm_bg.wasm 以wc为例,当前该wasm文件体积为23347b。\n代码层面 Link-Time Optimization (LTO) 是指在程序链接时进行的一种过程间优化(interprocedural optimization)。它允许编译器在链接阶段对多个编译单元进行优化,从而提高程序的性能、可靠性和安全性。\n从代码层面优化,主要是利用LTO(Link-Time Optimization)。\n代码内 在Cargo.toml中开启LTO:\n1 2 [profile.release] lto = true 开启LTO虽然能够减少编译后的体积,但是也会增加编译时间。\nLTO开启后,默认是在减少一定程度的编译体积的情况下,要确保编译的时间。如果你的需求就是更小的体积,而不是较短的时间,那么,可以通过手动指定编译等级来让LTO作出改变。\n在代码内可以使用如下等级:\ns:默认的 LTO 等级。它会进行最基本的 LTO 优化,包括内联函数、函数重写、数据重排等 z:最高级的 LTO 等级。它会进行更复杂的 LTO 优化,包括死代码消除、内存分配优化、安全性优化等 那么可以在Cargo.toml中这么配置:\n1 2 3 [profile.release] lto = true opt-level = \u0026#39;z\u0026#39; 原始的文件体积是23347b,现在编译后看一下体积:\n1 2 $ wc -c pkg/hello_wasm_bg.wasm 19879 pkg/hello_wasm_bg.wasm 很明显是减少体积!但是,使用z等级并不代表一定每次体积都会比s小的,有时候s也会比z小,这需要视代码情况而定。\n代码外 在代码外,可以使用wasm-opt来进行优化,其可以对 WebAssembly 模块进行多方面的优化,当然本篇文章中重点在体积方面(挖个坑,后面再详聊/狗头)。并且wasm-opt可以对所有符合WebAssembly规范的wasm文件进行优化,所以,就算你不是Rust写的,那也可以用其进行优化。(想想我曾经Go写的wasm,也是有多一个法子可以优化一把了\u0026hellip;\u0026hellip;)\n首先,来看一下wasm-opt的基本优化参数:\n-o:指定优化后的模块输出文件 -O:启用默认优化,等同于-Os参数 -O0:不进行任何优化 -O1:进行一些基本的优化,例如内联函数优化和死代码消除优化 -O2:进行更为彻底的优化,例如函数重写、数据重排、内存分配优化等 -O3:进行最为彻底的优化,包括一些可能影响程序功能的优化 -O4:与 -O3 相同,但会启用更为激进的优化 -Os:优化目标是减小代码大小,会进行一些可能影响性能的优化 -Oz:与 -Os 相同,但会启用更为激进的优化 基于本篇文章主题,此处将使用-Os和-Oz两种参数,其于上述\u0026quot;代码内\u0026quot;的等级是对应的。\n此处以原始wasm文件,以-Oz参数来执行一下,看一下对比效果:\n1 2 $ wc -c pkg/output.wasm 23194 pkg/output.wasm 再以上述开启“代码内“LTO编译后的wasm文件,以wasm-opt执行一下,看一下对比效果:\n1 2 $ wc -c pkg/output.wasm 19871 pkg/output.wasm 总体而言,wasm文件的体积越来越小。只是当前我这里的案例,是沿用系列文章内容的代码,没有什么实际性复杂代码,再者本身体积已经很小了,所以不会特别有效果。\n网络层面 网络层面的话,就是众所周知的在网络传输时,客户端和服务端约定相同的压缩算法,然后服务端给出时进行压缩,客户端接收时进行解压。网络层面可以对传输报文进行压缩,但不丢失信息。\n比如大家都很熟悉的gzip压缩算法,不过,压缩算法有好几种:\ngzip compress deflate br 其中gzip也是压缩率最高的了,此处就以gzip为例。\n在网络层面,将wasm文件以gzip压缩,减少其在传输时的体积。虽然减少了传输时的体积,但是浏览器在拿到压缩后的数据,需要消耗一定性能来解密。\n开启GZIP 开启GZIP其实简单,只要前后端约定好都用gzip就行了。\n首先,前端请求wasm文件时,需要在request header中放入浏览器支持的压缩模式:\n1 Accept-Encoding: gzip, deflate 接着,服务端收到这个请求后就可以给出服务端也支持的压缩模式,并告诉浏览器服务端将会用什么压缩模式。\n跟浏览器通信的方式就是将信息塞到respone header里面:\n1 Content-Encoding: gzip 这样就开启GZIP了。\n然后,就是浏览器接收到response的body和header,知道后端使用gzip压缩的,那么浏览器就会自动用gzip来解压,拿到完整的数据了。\n服务端支持 或许你会想问,浏览器能自动解密,那服务端怎么自动加密呢?要后端写代码让文件加密吗?\n那当然不是了,直接让http server来完成这个操作。此处以耳熟能详的Nginx为例。\n最简单的就是一行配置开启gzip了:\n1 gzip on; 也可以指定gzip的一些参数,比如可以加密的类型、最小加密长度等等:\n1 2 3 4 5 gzip on; gzip_types text/plain application/xml; gzip_proxied no-cache no-store private expired auth; gzip_min_length 1000; ... 更多的http server配置,可以去各自官方文档查阅。\n物理层面 你可能会惊奇,什么物理层面?!\n没错,真就是物理层面——直接对wasm文件进行gzip物理压缩!哈哈,这个方法也真是绝了,我之前在Go开发wasm时,寻找减少体积的时候发现的,如果你的wasm已经优化得穷途末路了,不妨大胆试试这个方案。😆\n还记得上面章节“网络层面“中,有个问题就是是否需要手动压缩,那么这里就是全程手动压缩和解压缩了,哈哈。\n物理压缩 首先,是对wasm文件进行物理层面的gzip压缩,此处先使用原始的wasm(23347b):\n1 gzip -c pkg/hello_wasm_bg.wasm \u0026gt; pkg/output.gz 然后,看一下其体积:\n1 2 $ wc -c pkg/output.gz 10403 pkg/output.gz 效果卓群,从23347b减少到了10403b!\n然后来把上述“代码层面”的优化来一遍,看一下最后的体积:\n1 2 $ wc -c pkg/output.gz 9237 pkg/output.gz 效果更加卓群了,从19871b减少到了9237b了!\n所以,此处就是对wasm文件进行物理压缩并存储,然后浏览器请求时,直接请求到.gz文件。\n物理解压 浏览器拿到.gz文件后,需要物理解压。\n这里推荐使用pako这个前端库,对.gz文件进行解压:\n1 2 3 4 5 6 7 8 9 async function gunzipWasm() { const res = await fetch(\u0026#34;target.gz\u0026#34;) let buffer = await pako.ungzip(await res.arrayBuffer()) // A fetched response might be decompressed twice on Firefox. // See https://bugzilla.mozilla.org/show_bug.cgi?id=610679 if (buffer[0] === 0x1F \u0026amp;\u0026amp; buffer[1] === 0x8B) {buffer = pako.ungzip(buffer)} return buffer } 之后就可以直接使用了。\nBUFF叠加 此处直接将上述所有方法都用起来,直接叠加buff,来看看当前(本系列文章积累的)这个案例能减少多少体积。在“物理层面”章节中,已经累加除了“网络层面”的buff了,所以可以直接使用其结果。而“网络层面”章节中,以gzip来压缩,将gzip的压缩率以40%来估算。\n那么最终该案例的wasm体积将在5542b,压缩率大约在77%!\n当然,还要算上一个初始的语言buff——Rust,使用Rust本身就已经导致wasm文件体积很小了。\n总结 本片文章中,从代码层面、网络层面、物理层面共三个层面介绍了对wasm文件的体积优化方案,其中共有四个方案。\n最后,当前(本系列文章积累的)该案例叠加了所有buff之后,能够减少77%的体积,真的感觉挺棒的了,哈哈。\n希望能够对各位有所帮助。\n","date":"2023-10-26T23:51:13Z","image":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg","permalink":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/","title":"使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积"},{"content":"前言 在上一篇文章《使用Rust和WebAssembly整花活儿(二)——DOM和类型转换》中,描述了使用Rust操作DOM,并实现Rust与JS类型转换的多种方法。\n在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。\n基于上一篇文章中,Rust与JS的类型转换的多种方法,本篇文章继续深入Rust与JS的交互。\n首先,Rust与JS的类型转换,可以实现变量的传递,那么变量是要用在哪里呢?那必然是函数了!\n所以,本篇文章来讲述一下Rust与JS的函数相互调用,基于此,可以实现大量日常功能开发。\n并且,还将会讲述一下,如何导出Rust的struct给JS调用。\n是的,没错,在JS调用Rust的struct!一开始看到这个功能的时候,我的脑子是有点炸裂的\u0026hellip;\u0026hellip;😳\n本篇文章中,将基于上一篇文章中创建的项目来继续开发。\n源码:github.com/Kuari/hello-wasm\n环境 Rust 1.70.0 wasm-bindgen 0.2.87 函数的相互调用 JS调用Rust函数 其实,在本系列文章的第一篇中,就是使用的JS调用Rust函数作为案例来演示的,这里依然以此为例,主要讲一下要点。\n首先,声明一个Rust函数:\n1 2 3 4 5 6 use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(a: i32, b: i32) -\u0026gt; i32 { a + b } 此处需要注意的要点如下:\n引入wasm_bindgen 声明一个函数,使用pub声明 在函数上使用#[wasm_bindgen]宏来将Rust函数导出为WebAssembly模块的函数 接着,编译成wasm文件:\n1 wasm-pack build --target web 然后,在JS中调用该函数:\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { add } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); const result = add(1, 2); console.log(`the result from rust is: ${result}`); } run(); \u0026lt;/script\u0026gt; 最后,启动http server,在浏览器的控制台中可以看到the result from rust is: 3,表明调用成功!\nRust调用JS函数 ###1 指定JS对象\n在Rust中调用JS函数,需要进行指定JS对象,也就是说,得明确告诉Rust,这个JS函数是从JS哪儿拿来的用的。\n主要在于下面两个方式:\njs_namespace: 是一个可选的属性,用于指定一个JavaScript命名空间,其中包含将要在wasm模块中导出的函数。如果没有指定js_namespace,则所有的导出函数将被放置在全局命名空间下。 js_name: 是另一个可选属性,它用于指定JavaScript中的函数名称。如果没有指定js_name,则导出函数的名称将与Rust中的函数名称相同。 ###2 JS原生函数\n对于一些JS原生函数,在Rust中,需要去寻找替代方案,比如我们上一篇文章中讲的console.log()函数,是不是觉得好麻烦啊!\n那么,你想直接在Rust中调用JS原生函数吗?!\n此处,就以console.log()函数为例,直接在Rust中引入并调用,免去替代方案的烦恼。\n首先,给出Rust代码:\n1 2 3 4 5 6 7 8 9 10 #[wasm_bindgen] extern \u0026#34;C\u0026#34; { #[wasm_bindgen(js_namespace = console)] fn log(message: \u0026amp;str); } #[wasm_bindgen] pub fn call_js_func() { log(\u0026#34;hello, javascript!\u0026#34;); } 如上代码中,call_js_func函数,顾名思义,此处是调用了js函数,并传入参数hello, javascript!。\n那么,call_js_func函数上方的代码,我们来一步步解析一下:\n第一行代码#[wasm_bindgen]是Rust的属性,它告诉编译器将函数导出为WebAssembly模块 extern \u0026quot;C\u0026quot;是C语言调用约定,它告诉Rust编译器将函数导出为C语言函数 #[wasm_bindgen(js_namespace = console)]告诉编译器将函数绑定到JavaScript中的console对象 fn log(message: \u0026amp;str)是一个Rust函数,它接受一个字符串参数,并将其打印到JavaScript中的console对象中 此处与JS交互的关键是js_namespace。在Rust中,js_namespace是用于指定JavaScript命名空间的属性。在WebAssembly中,我们可以通过它将函数绑定到JavaScript中的对象上。\n在上述代码中,#[wasm_bindgen(js_namespace = console)]告诉编译器将函数绑定到JavaScript中的console对象。这意味着在JS中使用console.log()函数来调用Rust中的log()函数。\n因此,类似的原生函数,都可以使用该方法来实现调用。\n最后,我们在JS中调用下:\n1 2 3 4 5 6 7 8 9 10 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { call_js_func } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); call_js_func(); } run(); \u0026lt;/script\u0026gt; 可以在浏览器的控制台中看到hello, javascript!。妙啊!\n其实对于console.log()而言,还有另一种调用方式,那就是使用js_namespace和js_name同时指定。\n或许,你会问,这有什么不同吗?是的,这有些不同。\n不知道你是否发现,当前这个案例中,指定了js_namespace为console,但是真实执行的函数是log(),那么这个log函数的指定,其实是体现在Rust中同样的函数名log。也就是说,该案例的log()就是console.log()中的log()。\n我们来换个名字看看,将原来的log()换成log2():\n1 2 3 4 5 6 7 8 9 10 #[wasm_bindgen] extern \u0026#34;C\u0026#34; { #[wasm_bindgen(js_namespace = console)] fn log2(message: \u0026amp;str); } #[wasm_bindgen] pub fn call_js_func() { log2(\u0026#34;hello, javascript!\u0026#34;) } 然后编译后去控制台看看,就会看到报错:\n1 TypeError: console.log2 is not a function 因此,当我们使用js_namespace和js_name结合的方式,在此处是可以进行自定义函数名的。\n看一下Rust代码:\n1 2 3 4 5 6 7 8 9 10 11 12 13 #[wasm_bindgen] extern \u0026#34;C\u0026#34; { #[wasm_bindgen(js_namespace = console)] fn log(message: \u0026amp;str); #[wasm_bindgen(js_namespace = console, js_name = log)] fn log_str(message: \u0026amp;str); } #[wasm_bindgen] pub fn call_js_func() { log_str(\u0026#34;hello, javascript!\u0026#34;) } 此处,重新定义了一个函数log_str,但是其指定了js_namespace = console和js_name = log,那么此处,就可以使用自定义的函数名。\n直接编译后,在控制台看一下,可以直接看到正常输出:hello, javascript!。\n总结一下,如果没有指定js_name,则 Rust 函数名称将用作 JS 函数名称。\n###3 自定义JS函数\n在一定场景下,需要使用Rust调用JS函数,比如对于一些对于JS而言更有优势的场景——用JS操作DOM,用Rust计算。\n首先,创建一个文件index.js,写入一个函数:\n1 2 3 export function addIt(m, n) { return m + n; }; 当前的文件结构关系如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 . ├── Cargo.lock ├── Cargo.toml ├── README.md ├── index.html ├── index.js ├── pkg │ ├── README.md │ ├── hello_wasm.d.ts │ ├── hello_wasm.js │ ├── hello_wasm_bg.wasm │ ├── hello_wasm_bg.wasm.d.ts │ └── package.json ├── src │ └── lib.rs └── target ├── CACHEDIR.TAG ├── debug ├── release └── wasm32-unknown-unknown 其中,index.js和lib.rs,以及hello_wasm_bg.wasm都是不在同一级别的,index.js都在其它两个文件的上一级。记住这个机构关系!\n然后,在lib.rs中,指定函数:\n1 2 3 4 ##./index.js\u0026#34;)] extern \u0026#34;C\u0026#34; { fn addIt(m: i32, n: i32) -\u0026gt; i32; } 其中,raw_module = \u0026quot;../index.js\u0026quot;的意思是,指定对应的index.js文件,大家应该清楚,此处指定的是刚刚创建的index.js。raw_module的作用就是用来指定js文件的。\n这段代码在前端,可以等同于:\n1 import { addIt } from \u0026#39;../index.js\u0026#39; 这样在前端都不用引入了,直接在Rust中引入了,感觉还有点奇妙的。\n接着,在Rust调用该函数:\n1 2 3 4 #[wasm_bindgen] pub fn call_js_func() -\u0026gt; i32 { addIt(1, 2) } 最后,在前端调用,编译后,在浏览器的控制台中可以看到输出结果了!\n总结一下,这里有几个注意点:\nJS的函数必须要export,否则将无法调用; raw_module只能用来指定相对路径,并且,大家可以在浏览器的控制台中注意到,此处的../的相对路径,其实是以wasm文件而言的相对路径,这里一定要注意呀! JS调用Rust的struct 现在,来点炸裂的,JS调用Rust的struct?!\nJS中连struct都没有,这玩意儿导出来会是什么样,得怎么在JS中调用呢?!\n首先,定义一个struct,并且声明几个方法:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #[wasm_bindgen] pub struct User { name: String, age: u32 } #[wasm_bindgen] impl User { #[wasm_bindgen(constructor)] pub fn new(name: String, age: u32) -\u0026gt; User { User { name, age } } pub fn print_user(\u0026amp;self) { log(format!(\u0026#34;name is : {}, age is : {}\u0026#34;, self.name, self.age).as_str()); } pub fn set_age(\u0026amp;mut self, age: u32) { self.age = age; } } 此处,声明了一个struct名为User,包含name和age两个字段,并声明了new、print_user和set_age方法。\n其中还有一个未见过的#[wasm_bindgen(constructor)],constructor用于指示被绑定的函数实际上应该转换为调用 JavaScript 中的 new 运算符。或许你还不太清晰,继续看下去,你就会明白了。\n接着,在JS中调用这个struct,和其方法:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; function addIt2(m, n) { return m + n; }; import init, { User } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); const user = new User(\u0026#39;kuari\u0026#39;, 20); user.set_age(21); user.print_user(); } run(); \u0026lt;/script\u0026gt; 可以看到,这里的用法就很熟悉了!\n大概想一下,在Rust中要如何调用?也就是直接new一个——User::new('kuari', 20)。\n此处在JS中,也是如此,先new一个!\n然后很自然地调用struct的方法。\n编译后,打开浏览器,可以在控制台看到输出:name is : kuari, age is : 21。\n其实,或许大家会很好奇,起码我是非常好奇的,Rust的struct在JS中到底是一个怎样的存在呢?\n这里直接添加一个console.log(user),就可以在输出看到。那么到底在JS中是一个怎样的存在呢?请各位动手打印一下看看吧!:P\n总结 本篇文章中,主要讲述了Rust与JS的交互,体现在Rust与JS的相互调用,这是建立在上一篇文章中类型转换的基础上的。\nRust与JS的函数相互调用的学习成本还是较大的,而且对比Go写wasm,Rust的颗粒度是非常细的,几乎可以说是随心所欲了。\n比较炸裂的就是Rust的struct导出给JS用,这对于Rust与JS的交互而言,还是非常棒的体验。\n","date":"2023-06-27T00:11:12Z","image":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg","permalink":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/","title":"使用Rust和WebAssembly整花活儿(三)——Rust与JS交互"},{"content":"前言 在上一篇文章《使用Rust和WebAssembly整花活儿(一)——快速开始》中,描述了如何创建项目和快速生成wasm并在前端中使用,迈出了整花活儿的第一步。\n在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。\n本篇文章中,将基于上一篇文章中创建的项目来继续开发。\n源码:github.com/Kuari/hello-wasm\n环境 Rust 1.70.0 wasm-bindgen 0.2.87 web-sys 0.3.64 DOM 配置依赖 要操作DOM,需要引入新的依赖web-sys,因此,可以配置Cargo.toml中依赖如下:\n1 2 3 [dependencies] wasm-bindgen = \u0026#34;0.2.87\u0026#34; web-sys = { version = \u0026#34;0.3.64\u0026#34;, features = [] } 你或许会好奇,这个features是什么,讲真,我一开始很好奇,又没看到什么特别的说明,试错才发现,原来是要手动引入功能依赖\u0026hellip;比如说,当你需要在Rust中使用JS的console,那么你需要在features中加入console。\n获取Document 在Rust中使用Document,我们需要按照上一步的说明,添加features。那么这里有一个依赖关系,首先在Rust中获取window,然后再获取document。\n因此,添加features后如下:\n1 2 3 [dependencies] wasm-bindgen = \u0026#34;0.2.87\u0026#34; web-sys = { version = \u0026#34;0.3.64\u0026#34;, features = [\u0026#34;Window\u0026#34;, \u0026#34;Document\u0026#34;] } 然后在lib.rs中创建一个函数,用来调用document:\n1 2 3 4 5 #[wasm_bindgen] pub fn update_message() { let window = web_sys::window().expect(\u0026#34;Failed to load window\u0026#34;); let document = window.document().expect(\u0026#34;Failed to load document\u0026#34;); } 那么,现在就是在Rust中解锁了document,就可以在前端为所欲为了!\n操作Element 那么开始操作一波,首先得获取到Element\u0026hellip;\u0026hellip;\n是的,你没有想错,继续来添加features吧,此处要添加一个Element:\n1 2 3 [dependencies] wasm-bindgen = \u0026#34;0.2.87\u0026#34; web-sys = { version = \u0026#34;0.3.64\u0026#34;, features = [\u0026#34;Window\u0026#34;, \u0026#34;Document\u0026#34;, \u0026#34;Element\u0026#34;] } ok,那么继续。此处设定函数传入两个参数selector和message,然后通过selector获取element,更新值为message参数的值。完整函数如下:\n1 2 3 4 5 6 7 8 9 10 11 12 #[wasm_bindgen] pub fn update_message(selector: \u0026amp;str, message: \u0026amp;str) { let window = web_sys::window().expect(\u0026#34;Failed to load window\u0026#34;); let document = window.document().expect(\u0026#34;Failed to load document\u0026#34;); let element = document.query_selector(selector).expect(\u0026#34;Failed to load element\u0026#34;); if let Some(element) = element { element.set_inner_html(message); } else { panic!(\u0026#34;Failed to set inner html\u0026#34;) } } 编译 将写完的Rust项目编译成wasm:\n1 wasm-pack build --target web 在html中调用 基于上一篇文章的项目中的html,此处添加一个div,id为message,添加调用wasm的update_message函数,代码如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;message\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { update_message } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; // 引入update_message函数 const run = async () =\u0026gt; { await init(); update_message(\u0026#39;#message\u0026#39;, \u0026#39;\u0026lt;h1\u0026gt;Hello, Rust!\u0026lt;/h1\u0026gt;\u0026#39;); // 调用update_message函数 } run(); \u0026lt;/script\u0026gt; 在浏览器验证 启动一个http server,然后在浏览器查看,可以看到在页面上出现一个h1标签的Hello, Rust!。\n发现更多方法 按照文章来写的过程中,你应该会发现一个问题——怎么这些方法没有补全?!\n是的,没错的,(至少我发现)当前web-sys并没有补全,所以只能结合开发者优秀的前端技能和丰富的官方文档来开发了。\nRust与JS的类型相互转换 对于wasm而言,性能固然是提升的,但是类型转换一直是个问题。当大量数据需要在wasm/js中进行类型转换时,这对性能来说,真的是个灾难。之前在使用go开发wasm时,就遇到过这样的问题,需要用官方的方法来进行手动类型转换,然而wasm处理的是一个很大的数据量\u0026hellip;\u0026hellip;\n不过好在Rust的类型支持真的挺丰富的!\n基础类型 基础类型挺简单的,而且Rust的范性也很好地支持了很多类型。如下是基础类型映射表:\nRust类型 JavaScript类型 i8 number i16 number i32 number i64 BigInt u8 number u16 number u32 number u64 BigInt f32 number f64 number bool boolean char string \u0026amp;str string String string \u0026amp;[T] 例如:\u0026amp;[u8] [T] 例如:Uint8Array Vec Array 基础类型转换示例 在lib.rs文件中,创建一个函数,挑选几个类型作为参数传入,然后将其读取并打印:\n1 2 3 4 5 6 7 8 9 10 11 12 13 #[wasm_bindgen] pub fn print_values(js_number: i32, js_boolean: bool, js_uint8_array: \u0026amp;[u8], js_number_array: Vec\u0026lt;i32\u0026gt;) { println!(\u0026#34;js number: {}\u0026#34;, js_number); println!(\u0026#34;js boolean: {}\u0026#34;, js_boolean); for item in js_uint8_array { println!(\u0026#34;js Uint8Array item: {}\u0026#34;, item); } for item in js_number_array { println!(\u0026#34;js number array item: {}\u0026#34;, item); } } 可以看到该函数传入了JS的number、boolean、Uint8Array和Array四个类型的参数。\n然后编译:\n1 wasm-pack build --target web 接着,在前端中引入函数并调用:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { print_values } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); const jsNumber = 10; const jsBoolean = true; const jsUint8Array = new Uint8Array(3); jsUint8Array[0] = 1; jsUint8Array[1] = 2; jsUint8Array[2] = 3; const jsNumberArray = [30, 40, 50]; print_values(jsNumber, jsBoolean, jsUint8Array, jsNumberArray); } run(); \u0026lt;/script\u0026gt; 最后,启动http server并打开浏览器,在控制台可以看到\u0026hellip;看不到?!\n是的,没错,Rust的println!只会将打印的内容发送到Rust的标准输出流,而不是前端的控制台。如果想在控制台中打印,那么需要调用JS的console了。\n使用新的功能,第一步就是添加features,Cargo.toml中添加console如下:\n1 2 3 [dependencies] wasm-bindgen = \u0026#34;0.2.87\u0026#34; web-sys = { version = \u0026#34;0.3.64\u0026#34;, features = [\u0026#34;Window\u0026#34;, \u0026#34;Document\u0026#34;, \u0026#34;Element\u0026#34;, \u0026#34;console\u0026#34;] } 在Rust中调用console.log()如下:\n1 web_sys::console::log_1(\u0026amp;\u0026#34;Hello, Rust!\u0026#34;.into()); 此处将其封装成一个函数:\n1 2 3 fn console_log(message: String) { web_sys::console::log_1(\u0026amp;message.into()); } 然后,将示例函数的println改成console_log()和format!,函数代码如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 #[wasm_bindgen] pub fn print_values(js_number: i32, js_boolean: bool, js_uint8_array: \u0026amp;[u8], js_number_array: Vec\u0026lt;i32\u0026gt;) { console_log(format!(\u0026#34;js number: {}\u0026#34;, js_number)); console_log(format!(\u0026#34;js boolean: {}\u0026#34;, js_boolean)); for item in js_uint8_array { console_log(format!(\u0026#34;js Uint8Array item: {}\u0026#34;, item)); } for item in js_number_array { console_log(format!(\u0026#34;js number array item: {}\u0026#34;, item)); } } 最后,编译之后,打开浏览器,就可以在控制台看到输出:\n1 2 3 4 5 6 7 8 js number: 10 js boolean: true js Uint8Array item: 1 js Uint8Array item: 2 js Uint8Array item: 3 js number array item: 30 js number array item: 40 js number array item: 50 通用类型 Rust中提供了一个通用的类型——JsValue,可以作为任何JS类型。\n这里给一个简单的案例,设置一个函数,使用JsValue作为参数传入,并打印。\n创建函数:\n1 2 3 4 #[wasm_bindgen] pub fn print_js_value(val: JsValue) { console_log(format!(\u0026#34;{:?}\u0026#34;, val)); } 然后编译成wasm文件。\n在html中调用:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { print_js_value } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); const jsNumber = 10; const jsBoolean = true; const jsUint8Array = new Uint8Array(3); jsUint8Array[0] = 1; jsUint8Array[1] = 2; jsUint8Array[2] = 3; const jsNumberArray = [30, 40, 50]; print_js_value(jsNumber); print_js_value(jsBoolean); print_js_value(jsUint8Array); print_js_value(jsNumberArray); } run(); \u0026lt;/script\u0026gt; 在html中,传入了不同类型的参数,但是在浏览器的控制台中可以看到,将所有不同类型的参数都打印出来了:\n1 2 3 4 JsValue(10) JsValue(true) JsValue(Uint8Array) JsValue([30, 40, 50]) Result Result在Rust中是一个很重要的存在,经常写Rust的话,也不想在写WebAssembly时改变开发习惯。\n其实对于JS而言,Result可以直接在catch中捕获到,只是说,这里我们需要定义好参数类型。\n####1 使用Result返回报错\n首先来一个只返回报错的场景:\n1 2 3 4 5 6 7 8 #[wasm_bindgen] pub fn only_return_error_when_result(count: i32) -\u0026gt; Result\u0026lt;(), JsError\u0026gt; { if count \u0026gt; 10 { Ok(()) } else { Err(JsError::new(\u0026#34;count \u0026lt; 10\u0026#34;)) } } 这里返回类型是Result,但是仅仅返回了一个错误。值得注意的是,这里的报错使用的类型是JsError,当然,这里也可以使用JsValue。\n然后在html调用:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { only_return_error_when_result } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); try { only_return_error_when_result(1); console.log(\u0026#39;1 is ok\u0026#39;); } catch(error) { console.log(\u0026#39;An error is reported when the input parameter is 1: \u0026#39;, error); } try { only_return_error_when_result(100); console.log(\u0026#39;100 is ok\u0026#39;); } catch(error) { console.log(\u0026#39;An error is reported when the input parameter is 100: \u0026#39;, error); } } run(); \u0026lt;/script\u0026gt; 这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了catch来捕获错误。\n那么,在浏览器的控制台可以看到输出:\n1 2 An error is reported when the input parameter is 1: Error: count \u0026lt; 10 100 is ok ####2 使用Result返回正常值和错误\n那么,如果想既返回正常值,也想返回错误呢?Rust返回一个Result是没有问题,那么JS怎么解析呢?\n直接上Rust代码:\n1 2 3 4 5 6 7 8 #[wasm_bindgen] pub fn return_all_when_result(count: i32) -\u0026gt; Result\u0026lt;i32, JsError\u0026gt; { if count \u0026gt; 10 { Ok(count + 10) } else { Err(JsError::new(\u0026#34;count \u0026lt; 10\u0026#34;)) } } 该函数,获取到参数后,如果满足条件,加10后返回,否则报错。\n那么看看html中如何调用:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { return_all_when_result } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); try { const res = return_all_when_result(1); console.log(`get ${res}`); } catch(error) { console.log(\u0026#39;An error is reported when the input parameter is 1: \u0026#39;, error); } try { const res = return_all_when_result(100); console.log(`get ${res}`); } catch(error) { console.log(\u0026#39;An error is reported when the input parameter is 100: \u0026#39;, error); } } run(); \u0026lt;/script\u0026gt; 是的,没错,正常获取就行了\u0026hellip;\u0026hellip;/捂脸哭\n这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了catch来捕获错误。\n最后,就是在浏览器的控制台中看到:\n1 2 An error is reported when the input parameter is 1: Error: count \u0026lt; 10 get 110 直接引入JS类型 如果你想更直接一点,那么可以直接引入JS类型!这里主要是利用js-sys这个依赖,可以在官方文档上看到很多JS的类型和函数,直接引入即可使用。当然,一定场景下,直接引入的类型,是需要手动转换类型的。\n####1 配置依赖\n在Cargo.toml中添加js-sys依赖:\n1 2 3 4 [dependencies] wasm-bindgen = \u0026#34;0.2.87\u0026#34; web-sys = { version = \u0026#34;0.3.64\u0026#34;, features = [\u0026#34;Window\u0026#34;, \u0026#34;Document\u0026#34;, \u0026#34;Element\u0026#34;, \u0026#34;console\u0026#34;] } js-sys = \u0026#34;0.3.61\u0026#34; ####2 Uint8Array\n首先以Uint8Array举例,在lib.rs头部引入类型:\n1 use js_sys::Uint8Array; 然后创建一个函数,参数和返回都是Uint8Array类型:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #[wasm_bindgen] pub fn print_uint8_array(js_arr: Uint8Array) -\u0026gt; Uint8Array { // new Uint8Array let mut arr = Uint8Array::new_with_length(3); // Uint8Array -\u0026gt; vec for (index, item) in js_arr.to_vec().iter().enumerate() { console_log(format!(\u0026#34;{} - the item in js_arr: {}\u0026#34;, index, item)); } // Avoid type conversion // Use the method of the type itself for index in 0..js_arr.length() { console_log(format!(\u0026#34;{} - the item in js_arr: {}\u0026#34;, index, js_arr.get_index(index))); } // vec -\u0026gt; Uint8Array let vec = vec![1, 2, 3]; let arr2 = Uint8Array::from(vec.as_slice()); arr = arr2.clone(); // Use the method of the type itself arr.set_index(0, 100); arr } 忽略该函数中无意义的逻辑和arr变量的警告,只是为了演示用法。\n可以在代码中看到,直接引入的Uint8Array有自己的方法,一定场景下,需要转换类型,但是最好避免进行类型转换,而直接使用其自带的方法。\n这里可以简要总结下,就是最好一定场景内全部使用直接引入的JS类型,或者直接全部使用Rust类型来代替JS类型,两者都存在场景下,手动转换类型是件很糟糕的事。\n####3 Date\nDate类型,在上面的篇章中都没有提及,这里可以直接引入JS的Date类型来使用。\n首先是引入类型:\n1 use js_sys::Date; 然后,创建一个函数,返回时间戳:\n1 2 3 4 5 #[wasm_bindgen] pub fn return_time() -\u0026gt; f64 { let date = Date::new_0(); date.get_time() } 接着,在html中调用:\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { return_time } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); console.log(\u0026#39;current time: \u0026#39;, return_time()); } run(); \u0026lt;/script\u0026gt; 最后,在浏览器的控制台中,可以看到:\n1 current time: 1686979932833 总结 本文中,主要讲述了如何使用Rust来实现DOM操作,读者可以根据方法自己去找到合适的方法,来实现自己的场景。其次,还讲述了Rust与JS的类型转换,从基础的各自类型的映射,到Rust独有的Result,到直接引入JS类型。当然这里需要注意的是,直接引入JS类型和Rust的基础类型映射JS类型这两种方法尽量不要混用,混用会导致需要手动类型转换,造成性能损耗。\n至此,又向Rust和WebAssembly整花活儿迈进了一步~😼\n","date":"2023-06-18T18:18:31Z","image":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg","permalink":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/","title":"使用Rust和WebAssembly整花活儿(二)——DOM和类型转换"},{"content":"前言 WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。\n之前写过一篇文章,是关于如何使用golang来开发WebAssembly的——WebAssembly:未来前端开发的必备技能。\nRust和Go都可以用来开发WebAssembly,但它们有各自的优势和劣势。\nRust的优点:\n更快的性能和更小的二进制文件 更好的内存安全性 Go的优点:\n更容易上手和学习 更好的生态系统和社区支持 综合来说,如果你更注重性能和内存安全性,那么Rust可能是更好的选择。而如果你更注重开发效率和易用性,那么Go可能更适合你。当然,实际情况还需要根据具体的项目需求和团队情况来选择。\n因为一些工作需求,最近整了些rust的花活儿,这里系统地记录一下。当你遇到更注重性能和内存安全性的场景,希望这能有帮助。\n环境 Rust 1.70.0 wasm-bindgen 0.2.87 创建项目并添加依赖 此处默认已经安装Rust,需要安装的小伙伴儿可以参考官网。\n使用Cargo创建一个名为hello-wasm的项目:\n1 cargo new --lib hello-wasm 进入项目,打开文件Cargo.toml,添加依赖:\n1 2 3 4 5 [lib] crate-type = [\u0026#34;cdylib\u0026#34;] [dependencies] wasm-bindgen = \u0026#34;0.2.87\u0026#34; 更新lib.rs 默认创建项目中,存在一个名为lib.rs的文件,将内容全部替换成:\n1 2 3 4 5 6 use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(a: i32, b: i32) -\u0026gt; i32 { a + b } 编译 至此,我们创建了一个最简单的功能——一个返回两个整数相加结果的函数。\n然后我们可以进行编译了,编译之前需要安装一个工具wasm-pack:\n1 cargo install wasm-pack 然后进行编译:\n1 wasm-pack build --target web 编译完成之后,将会多出来一个pkg文件夹,内容如下:\n1 2 3 4 5 6 pkg ├── hello_wasm.d.ts ├── hello_wasm.js ├── hello_wasm_bg.wasm ├── hello_wasm_bg.wasm.d.ts └── package.json 虽然文件很多,但是首先我们看到了我们所需要的wasm文件,并且,根据go的wasm引入方式,这里我们或许会需要用到js文件。\n前端引入 为了方便最快校验,直接在hello-wasm项目中创建index.html文件,来进行前端引入。\n创建index.html 那么,首先,创建index.html文件:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34;\u0026gt; \u0026lt;title\u0026gt;使用Rust和WebAssembly整花活儿\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; Hello, World! \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 是的,没错!这是一场标准的开局!😼\n引入WASM 其实不同于go语言的wasm引入方式,Rust更希望直接引入js文件,而不是让开发者手动引入wasm文件。\n这里使用js引入:\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { add } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); const result = add(1, 2); console.log(`the result from rust is: ${result}`); } run(); \u0026lt;/script\u0026gt; 完整代码 完整的html代码如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34;\u0026gt; \u0026lt;title\u0026gt;使用Rust和WebAssembly整花活儿\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; Hello, World! \u0026lt;/body\u0026gt; \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import init, { add } from \u0026#39;./pkg/hello_wasm.js\u0026#39;; const run = async () =\u0026gt; { await init(); const result = add(1, 2); console.log(`the result from rust is: ${result}`); } run(); \u0026lt;/script\u0026gt; \u0026lt;/html\u0026gt; 验证 这里可以快速起一个http server,这里我选择使用http-server,也可以使用python3 -m http.server这样的方式,看怎么各自的使用习惯。\n那么,启动http server:\n1 http-server 打开浏览器,访问http://localhost:8080,打开调试器,即可看到输出the result from rust is: 3,这就意味着迈出了整花活儿的第一步!\n常见问题 前端报响应类型错误 详细报错如下:\n1 Failed to load module script: The server responded with a non-JavaScript MIME type of \u0026#34;application/wasm\u0026#34;. Strict MIME type checking is enforced for module scripts per HTML spec. 当引入WebAssembly生成的js文件时,可能会遇到这个报错。报错乍一看是http server的响应问题,或者搜索时候,也会有帖子说这是一个response问题。\n实际上,当按照这个文章一步步操作时是不会有这个问题的,是因为本文的编译参数是直接解决了这个问题的。当我自己摸索的时候,解决这个问题真的是看到人都麻了……\n关键在于编译命令的参数:--target。\n当没有设置这个参数时,默认的参数其实是--target bundler,其是编译成给webpack之类的脚手架使用的。因此这里使用—target web,则是使其编译成可直接在web中使用。\n相关参数如下:\nbundler:编译成给webpack之类的脚手架使用 web:编译成web可直接使用 nodejs:编译成可通过require来加载的node模块 deno:编译成可通过import加载的deno模块 no-modules:跟web类似,但是更旧,且不能使用es模块 直接引入wasm文件 若此时尝试直接引入wasm文件,而不是使用本文所述的方式,那么你会发现,也是可行的!\n1 2 3 4 5 \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; WebAssembly.instantiateStreaming(fetch(\u0026#34;./pkg/hello_wasm_bg.wasm\u0026#34;), {}).then( (obj) =\u0026gt; console.log(\u0026#39;the result from rust is: \u0026#39;, obj.instance.exports.add(1, 2)) ); \u0026lt;/script\u0026gt; 是的,没错,当前是可行的,但是当引入了一些别的比如dom之类的,就坏起来了……\n更新的wasm引入方式 上一问题中,且不说是否可以直接引入wasm文件,这里仅说一下,instantiateStreaming这个方法。这是一个更新的方法,无需转成arrayBuffer,这也是摸索Rust整活儿时候发现的。如果在别的语言引入wasm,请使用这个更新的方法吧。\n","date":"2023-06-14T17:32:28Z","image":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/cover_hu822cb824dae9cb9cc6f33de0e5ba4a75_1034399_120x120_fill_q75_box_smart1.jpg","permalink":"https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/","title":"使用Rust和WebAssembly整花活儿(一)——快速开始"},{"content":"第三方库的痛苦 在日常的前端开发中,经常需要将一些数据从网页上复制到剪切板中。而实现复制功能,第一时间想到的就是引入第三方库。\n曾经过多不少第三方的剪切板的库,是真的很繁琐,又是创建对象,又是绑定DOM,头都要炸了,就个简单的复制功能,第三方库换来换去地测试\u0026hellip;\u0026hellip;\n后来看到了vueuse可以直接用,突然觉得,哇!真棒!\n直到有一天,搜到了Clipboard api\u0026hellip;\u0026hellip;\n原生支持 官方文档:https://developer.mozilla.org/en-US/docs/Web/API/Clipboard\n不管是读,还是写,统统搞定!而且都还是异步方法。\n比如复制文本到剪切板:\n1 navigator.clipboard.writeText(\u0026#34;\u0026lt;empty clipboard\u0026gt;\u0026#34;) 复制canvas到剪切板:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 function copyCanvasContentsToClipboard(canvas, onDone, onError) { canvas.toBlob((blob) =\u0026gt; { let data = [new ClipboardItem({ [blob.type]: blob })]; navigator.clipboard.write(data).then( () =\u0026gt; { onDone(); }, (err) =\u0026gt; { onError(err); } ); }); } 读取剪切板的文本:\n1 const txt = navigator.clipboard.readText() ","date":"2023-03-15T22:39:35Z","permalink":"https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/","title":"原来浏览器原生支持JS复制到剪切板"},{"content":"前言 前端从后端获取到sts,然后直接minio,极大减少服务端的压力。\n当然,肯定会这种疑问,为什么不在后端生成临时签名url,给前端上传/下载呢?问就是业务需要 /狗头\u0026hellip;\u0026hellip;\n环境 minio: ^7.0.32 typescript: 4.9.5 浏览器上的坑 为什么标题上要强调“浏览器”呢?\n这是因为官方的minio.js,感觉当前版本并没有考虑浏览器的使用场景,无法直接在浏览器上直传。\n所以这里就是推荐一个折中的方案,可以在前端直传。\n当然了,如果业务允许,当前版本请直接在后端生成临时签名url吧!\nminio直传代码实现 安装minio.js 1 pnpm add -D minio 生成client 1 2 3 4 5 6 7 8 9 10 11 import * as Minio from \u0026#39;minio\u0026#39; const minioClient = new Minio.Client({ region: \u0026#39;cn-north-1\u0026#39;, // region字段,极其有必要加上!我在sts场景,不加region字段就报错权限不够/心累... endPoint: \u0026#39;192.168.1.1\u0026#39;, // minio的地址 port: 9000, // minio端口 useSSL: true, // 是否使用ssl accessKey, secretKey, sessionToken, // 可选字段,当为sts时,加入此字段 }) putObject直传方案 1 putObject(bucketName, objectName, stream) 官方提供的putObject方法,必填这三个字段,前两个很好理解,主要是第三个stream,需要详细看一下。\nstream的类型是:string | internal.Readable | Buffer\nstring很好搞定,直接putObject(bucketName, 'hello.txt', 'hello,world!')这样子上传文本文件。\n但是另外两个类型都是nodejs的啊\u0026hellip;啊这\u0026hellip;(也许是我错了,有大佬能够解决的请务必直接告诉我\u0026hellip;)\n折中方案 那么折中的方案就是由前端生成临时签名url,再由前端进行上传 /哭。\n1 const url = await minioClient.presignedPutObject(bucketName, filename) 然后利用http请求url进行上传即可。虽然比较曲折,但是目前相对比较好的前端解决方案。\nhttp请求url上传 此处介绍下http请求上传的相关操作。\n请求上传 请求上传可以有三种方案。\nXMLHttpRequest 1 2 3 4 5 6 7 8 9 10 11 12 const xhrUploadFile = (file: File, url: string) =\u0026gt; { const xhr = new XMLHttpRequest(); xhr.open(\u0026#39;PUT\u0026#39;, url, true); xhr.send(file); xhr.onload = () =\u0026gt; { if (xhr.status === 200) { console.log(`${file.name} 上传成功`); } else { console.error(`${file.name} 上传失败`); } }; } Fetch 1 2 3 4 5 6 7 8 9 10 11 12 const fetchUploadFile = (file: File, url: string) =\u0026gt; { fetch(url, { method: \u0026#39;PUT\u0026#39;, body: file, }) .then((response) =\u0026gt; { console.log(`${file.name} 上传成功`, response); }) .catch((error) =\u0026gt; { console.error(`${file.name} 上传失败`, error); }); } Axios 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const axiosUploadFile = (file: File, url: string) =\u0026gt; { const instance = axios.create(); instance .put(url, file, { headers: { \u0026#39;Content-Type\u0026#39;: file.type, }, }) .then(function (response) { console.log(`${file.name} 上传成功`, response); }) .catch(function (error) { console.error(`${file.name} 上传失败`, error); }); } Promise 此处可以封装一下请求,叠加promisebuff,此处以XMLHttpRequest为例:\n1 2 3 4 5 6 7 8 9 10 11 12 13 const uploadRequest = (file: File, url: string) =\u0026gt; { return new Promise((resolve, reject) =\u0026gt; { const xhr = new XMLHttpRequest() xhr.open(\u0026#39;PUT\u0026#39;, url, true) xhr.send(file) xhr.onload = () =\u0026gt; { if (xhr.status === 200) resolve(xhr.response) else reject(xhr.status) } }) } 事件响应 要完成上传,怎么能没有响应事件呢。\n此处的事件包括:\n上传进度 上传完成 上传失败 请求代码如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const uploadRequest = async (file: File, url: string) =\u0026gt; { const xhr = new XMLHttpRequest() xhr.upload.addEventListener(\u0026#39;progress\u0026#39;, (e) =\u0026gt; { onUploadProgress(`${((e.loaded / e.total) * 100).toFixed(0)}`) // 更新进度,此处不保留小数点 }) xhr.onload = () =\u0026gt; { if (xhr.status === 200) { try { onUploaded() // 响应上传完成事件 } catch (error) { onUploadErr((error as Error).message) // 响应上传错误,此处ts处理error } } else { onUploadErr(`http code is ${xhr.status}`) } // 响应上传错误 } xhr.open(\u0026#39;PUT\u0026#39;, url, true) xhr.send(file) } 取消上传 1 2 3 const xhr = new XMLHttpRequest() // ... xhr.abort() // 取消上传 性能优化 对于大文件,或者说需要后台运行的上传任务,可以使用web worker来跑 对于大文件,可以使用切片的方式提高并发,切片的方式也可以实现断点续传,只是需要注意文件切片的顺序和唯一id 参考文档 github.com/minio/minio-js ","date":"2023-02-17T13:31:56Z","permalink":"https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/","title":"浏览器上ts实现前端直传minio"},{"content":"前言 WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。\n快速上手 用go写一个hello world 1 2 3 4 5 6 7 8 9 package main import ( \u0026#34;fmt\u0026#34; ) func main() { fmt.Println(\u0026#34;Hello, WebAssembly!\u0026#34;) } 将go文件编译成wasm文件 1 GOOS=js GOARCH=wasm go build -o static/main.wasm 拷贝出wasm_exec.js 该文件为go的wasm的js支持文件\n1 cp \u0026#34;$(go env GOROOT)/misc/wasm/wasm_exec.js\u0026#34; static html文件调用wasm文件 1 2 3 4 5 6 \u0026lt;script src=\u0026#34;static/wasm_exec.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; const go = new Go(); WebAssembly.instantiateStreaming(fetch(\u0026#34;static/main.wasm\u0026#34;), go.importObject) .then((result) =\u0026gt; go.run(result.instance)) \u0026lt;/script\u0026gt; 验证调用 浏览器加载html文件,f12打开控制台,可以看到wasm的打印消息。\ngo与js的类型转换 类型映射 1 2 3 4 5 6 7 8 9 10 | Go | JavaScript | | ---------------------- | ---------------------- | | js.Value | [its value] | | js.Func | function | | nil | null | | bool | boolean | | integers and floats | number | | string | string | | []interface{} | new array | | map[string]interface{} | new object | 如上为官方给出的go与js的类型映射表。\n比如在go中调用js函数,参数为array,那么就可以直接将go的[]interface{}类型的变量作为参数使用。\n函数转换数组 syscall/js提供了两个函数:\nCopyBytesToGo:func CopyBytesToGo(dst []byte, src Value) int CopyBytesToJS:func CopyBytesToJS(dst Value, src []byte) int 两者对于go而言,类型都是[]byte,但是对于js而言,需要Uint8Array或者Uint8ClampedArray类型,否则就会报错。\n那么,如何在go中生成一个Uint8Array或者Uint8ClampedArray类型的变量呢?官方的类型映射表也没有啊\u0026hellip;那么就看下一步。\n其余类型 对于非官方类型映射表内的类型,和官方提供的两个数据类型转换之外的类型,可以通过一种通用的方式来生成,以上一步的Uint8Array为例:\n1 js.Global().Get(\u0026#34;Uint8Array\u0026#34;).New(\u0026lt;length\u0026gt;) 实际使用案例:\n1 2 3 // goData []byte{...} jsData := js.Global().Get(\u0026#34;Uint8Array\u0026#34;).New(len(goData)) js.CopyBytesToJS(jsData, goData) 那么,比如js中的Date类型:\n1 2 dateConstructor := js.Global().Get(\u0026#34;Date\u0026#34;) dateConstructor.New(\u0026#34;2020-10-01\u0026#34;) 极端情况 好吧,还有最后一个方案,如果遇到极端情况,上述方案都无法解决,那么请转换成字符串吧!让go和js用各自的方法分别处理一波,得到自己想要的结果或者给出各自想给的数据。\njs调用go函数 此处需要在go中引入syscall/js,以实现js相关的操作。\n注册go函数 将go的函数注册为js的函数,由js来进行调用。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 package main import \u0026#34;syscall/js\u0026#34; func handleCount(this js.Value, args []js.Value) interface{} { count := args[0].Int() return js.ValueOf(count + 1) } func main() { done := make(chan string, 0) js.Global().Set(\u0026#34;HandleEvent\u0026#34;, js.FuncOf(handleEvent)) \u0026lt;-done } js.Func() 接受一个函数类型作为其参数,该函数的定义是固定的:\n1 2 3 4 func(this Value, args []Value) interface{} // this 即 JavaScript 中的 this // args 是在 JavaScript 中调用该函数的参数列表。 // 返回值需用 js.ValueOf 映射成 JavaScript 的值 js.ValueOf返回作为js的值:\n1 2 3 4 5 6 7 8 9 10 | Go | JavaScript | | ---------------------- | ---------------------- | | js.Value | [its value] | | js.Func | function | | nil | null | | bool | boolean | | integers and floats | number | | string | string | | []interface{} | new array | | map[string]interface{} | new object | js调用 在js中使用也非常简单,引入wasm文件之后,直接调用函数即可。\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;script src=\u0026#34;static/wasm_exec.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; const go = new Go(); WebAssembly.instantiateStreaming(fetch(\u0026#34;static/main.wasm\u0026#34;), go.importObject) .then((result) =\u0026gt; go.run(result.instance)) \u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.querySelector(\u0026#39;#button\u0026#39;).addEventListener(\u0026#39;click\u0026#39;, () =\u0026gt; { HandleEvent(1) // 传入参数1 }) \u0026lt;/script\u0026gt; go调用js函数 如果在js中本身已经定义了函数,那么在go中也可以直接调用该函数,进行运算,将得出的结果在go中继续使用。\n定义js函数 1 2 3 4 5 \u0026lt;script\u0026gt; function add(m, n) { return m + n } \u0026lt;/script\u0026gt; go中调用js函数 1 2 3 4 5 6 7 8 9 10 11 12 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;syscall/js\u0026#34; ) func main() { result := js.Global().Call(\u0026#34;add\u0026#34;, 1, 2) fmt.Println(result) // 此处输出类型为js.Value,无法直接使用 fmt.Println(result.Int() + 1) // 使用.Int()将其转换为go中的类型,即可直接使用 } 引入wasm 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;script\u0026gt; function add(m, n) { return m + n } \u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;static/wasm_exec.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; const go = new Go(); WebAssembly.instantiateStreaming(fetch(\u0026#34;static/main.wasm\u0026#34;), go.importObject) .then((result) =\u0026gt; go.run(result.instance)) \u0026lt;/script\u0026gt; 结果 在前端调试台可以看到输出:\n1 2 \u0026lt;number: 3\u0026gt; 4 第一个结果就是js.Value的值,第二个结果则是转换成go的值,并按照逻辑进行了+1处理。\n回调函数/解决go函数阻塞问题 The Go function fn is called with the value of JavaScript\u0026rsquo;s \u0026ldquo;this\u0026rdquo; keyword and the arguments of the invocation. The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.\nInvoking the wrapped Go function from JavaScript will pause the event loop and spawn a new goroutine. Other wrapped functions which are triggered during a call from Go to JavaScript get executed on the same goroutine.\nAs a consequence, if one wrapped function blocks, JavaScript\u0026rsquo;s event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.\nsyscall/js官方文档表明,如果go包装函数阻塞,那么js的事件循环也将被阻塞,直到函数返回,调用任何需要事件循环(如fetch)的异步js api都导致立即死锁。因此,一个阻塞函数应该显式地启动一个新的协程。\n此处,可以在go中注册一个回调函数,加上协程实现异步,不会产生堵塞。\n注册函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;syscall/js\u0026#34; \u0026#34;time\u0026#34; ) func handleRender(this js.Value, args []js.Value) interface{} { username := args[0].String() callback := args[len(args)-1] go func() { time.Sleep(3 * time.Second) callback.Invoke(fmt.Sprintf(\u0026#34;hello, %s !\u0026#34;, username)) }() fmt.Println(\u0026#34;waiting...\u0026#34;) return nil } func main() { done := make(chan string, 0) js.Global().Set(\u0026#34;HandleRender\u0026#34;, js.FuncOf(handleRender)) \u0026lt;-done } js调用 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;script src=\u0026#34;static/wasm_exec.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; const go = new Go(); WebAssembly.instantiateStreaming(fetch(\u0026#34;static/main.wasm\u0026#34;), go.importObject) .then((result) =\u0026gt; go.run(result.instance)) \u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.querySelector(\u0026#39;#button\u0026#39;).addEventListener(\u0026#39;click\u0026#39;, () =\u0026gt; { HandleRender(\u0026#34;tom\u0026#34;, (message) =\u0026gt; console.log(\u0026#39;message: \u0026#39;, message)) }) \u0026lt;/script\u0026gt; 输出 在浏览器调试台,可以看到:\n1 2 waiting... // 先输出了waiting... hello, tom ! // 隔了3秒之后,输出了回调函数的值 Go实现Promise 上一步的回调函数,解决了函数阻塞问题,此处,结合回调函数实现promise,来丰富异步场景。\n在js中,promise是这样的:\n1 2 3 4 5 const message = new Promise((resolve, reject) =\u0026gt; { setTimeout(() =\u0026gt; { resolve(\u0026#34;hello, world !\u0026#34;) }, 3000) }) 使用async和await调用,拿到结果:\n1 2 3 4 5 6 7 8 9 10 async function printMessage() { const message = new Promise((resolve, reject) =\u0026gt; { setTimeout(() =\u0026gt; { resolve(\u0026#34;hello, world !\u0026#34;) }, 3000) }) const result = await message console.log(result) } 在go中又如何构建promise呢?这里可以用到上述go与js的类型转换,创建一个promise:\n1 js.Global().Get(\u0026#34;Promise\u0026#34;) 注册函数 go的完整实现如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;syscall/js\u0026#34; \u0026#34;time\u0026#34; ) var document = js.Global().Get(\u0026#34;document\u0026#34;) func handleRender(this js.Value, args []js.Value) interface{} { handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { resolve := args[0] go func() { time.Sleep(3 * time.Second) resolve.Invoke(\u0026#34;hello, world !\u0026#34;) }() fmt.Println(\u0026#34;waiting...\u0026#34;) return nil }) promiseConstructor := js.Global().Get(\u0026#34;Promise\u0026#34;) return promiseConstructor.New(handler) } func main() { done := make(chan string, 0) js.Global().Set(\u0026#34;HandleRender\u0026#34;, js.FuncOf(handleRender)) \u0026lt;-done } js调用 1 2 3 4 5 6 7 8 9 10 11 12 \u0026lt;script src=\u0026#34;static/wasm_exec.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; const go = new Go(); WebAssembly.instantiateStreaming(fetch(\u0026#34;static/main.wasm\u0026#34;), go.importObject) .then((result) =\u0026gt; go.run(result.instance)) \u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.querySelector(\u0026#39;#button\u0026#39;).addEventListener(\u0026#39;click\u0026#39;, async () =\u0026gt; { const message = await HandleRender() console.log(\u0026#39;message: \u0026#39;, message) }) \u0026lt;/script\u0026gt; 输出 在浏览器调试台,可以看到:\n1 2 waiting... // 先输出了waiting... message: hello, world ! // 隔了3秒之后输出 操作DOM 使用document 定义一个全局的document\n1 var docuemnt = js.Global().Get(\u0026#34;document\u0026#34;) 获取元素 获取一个id为container的div,设置background-color: red、widht: 600、height: 400\n1 2 3 4 5 var containerElement = document.Call(\u0026#34;getElementById\u0026#34;, \u0026#34;container\u0026#34;) var containerElementStyle = container.Get(\u0026#34;style\u0026#34;) containerElementStyle.Set(\u0026#34;background\u0026#34;, \u0026#34;red\u0026#34;) containerElementStyle.Set(\u0026#34;width\u0026#34;, \u0026#34;600px\u0026#34;) containerElementStyle.Set(\u0026#34;height\u0026#34;, \u0026#34;400px\u0026#34;) 创建元素 创建一个id为image的image,设置width:300、height:200\n1 2 3 var imageElement = document.Call(\u0026#34;createElement\u0026#34;, \u0026#34;canvas\u0026#34;) imageElement.Set(\u0026#34;width\u0026#34;, 300) imageElement.Set(\u0026#34;width\u0026#34;, 200) 添加子元素 将image添加为id为container的div的子元素\n1 containerElement.Call(\u0026#34;appendChild\u0026#34;, imageElement) 添加事件 给image添加右击事件,右击image则阻止右键菜单\n1 2 3 4 5 6 7 8 // 定义响应函数 func handlePreventEventCallBack(this js.Value, args []js.Value) interface{} { args[0].Call(\u0026#34;preventDefault\u0026#34;) return false } // 给image添加事件 imageElement.Call(\u0026#34;addEventListener\u0026#34;, \u0026#34;contextmenu\u0026#34;, js.FuncOf(handlePreventEventCallBack)) 这里需要注意的是,当不再调用响应事件函数时,必须调用Func.Release以释放资源:\n1 2 3 4 5 6 7 var cb js.Func cb = js.FuncOf(func(this js.Value, args []js.Value) any { fmt.Println(\u0026#34;button clicked\u0026#34;) cb.Release() // 如果不再单击该按钮,则释放该函数 return nil }) js.Global().Get(\u0026#34;document\u0026#34;).Call(\u0026#34;getElementById\u0026#34;, \u0026#34;myButton\u0026#34;).Call(\u0026#34;addEventListener\u0026#34;, \u0026#34;click\u0026#34;, cb) Canvas 这里放一波canvas的案例,包含了一些常用方法,可以参考完成更多操作。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package main import ( \u0026#34;math\u0026#34; \u0026#34;syscall/js\u0026#34; ) const ( width = 400 height = 400 radius = 200 ) var document = js.Global().Get(\u0026#34;document\u0026#34;) func handleRender(this js.Value, args []js.Value) interface{} { var canvas = document.Call(\u0026#34;getElementById\u0026#34;, \u0026#34;canvas\u0026#34;) canvas.Set(\u0026#34;width\u0026#34;, width) canvas.Set(\u0026#34;height\u0026#34;, height) var ctx = canvas.Call(\u0026#34;getContext\u0026#34;, \u0026#34;2d\u0026#34;) ctx.Call(\u0026#34;beginPath\u0026#34;) ctx.Call(\u0026#34;arc\u0026#34;, width/2, height/2, radius, 0, 2*math.Pi) ctx.Set(\u0026#34;fillStyle\u0026#34;, \u0026#34;lightpink\u0026#34;) ctx.Call(\u0026#34;fill\u0026#34;) ctx.Set(\u0026#34;lineWidth\u0026#34;, 2) ctx.Set(\u0026#34;strokeStyle\u0026#34;, \u0026#34;red\u0026#34;) ctx.Call(\u0026#34;stroke\u0026#34;) ctx.Set(\u0026#34;font\u0026#34;, \u0026#34;20px Comic Sans MS\u0026#34;) ctx.Set(\u0026#34;fillStyle\u0026#34;, \u0026#34;blue\u0026#34;) ctx.Call(\u0026#34;fillText\u0026#34;, \u0026#34;Hello, World !\u0026#34;, width/2-60, height/2) return nil } func main() { done := make(chan string, 0) js.Global().Set(\u0026#34;HandleRender\u0026#34;, js.FuncOf(handleRender)) \u0026lt;-done } 渲染结果:\n参考文档 syscall/js go wiki WebAssembly Go WebAssembly (Wasm) 简明教程 Go, WebAssembly, HTTP requests and Promises ","date":"2023-02-09T13:15:34Z","image":"https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/cover_hu18c5fb7037479757471f623d2395d33e_660360_120x120_fill_q75_box_smart1.jpg","permalink":"https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/","title":"WebAssembly -- 未来前端开发的必备技能"},{"content":"环境 vue 3.2 typescript 4.7.4 wow.js 1.2.2 animate.css 4.1.1 animate.css 下载 1 pnpm add animate.css -D 引入 在vue3项目的main.ts中引入\n1 import \u0026#39;animate.css\u0026#39; 使用 需要注意的是,animate css在4.0之后使用animate__前缀\n1 \u0026lt;h1 class=\u0026#34;animate__animated animate__bounce\u0026#34;\u0026gt;An animated element\u0026lt;/h1\u0026gt; 动画延迟 官方方法 官方给出的动画延迟是animate__delay-2s、animate__delay-3s \u0026hellip;\u0026hellip;\n直接在class中添加即可\n1 \u0026lt;h1 class=\u0026#34;animate__animated animate__bounce animate__delay-2s\u0026#34;\u0026gt;An animated element\u0026lt;/h1\u0026gt; 自定义延迟 特殊场景需要使用不同于官方的延迟时间,因此可以自定义延迟时间,直接声明延迟的类,然后在class上加入即可\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .animation-delay-1 { animation-delay: 100ms; } .animation-delay-2 { animation-delay: 300ms; } .animation-delay-3 { animation-delay: 500ms; } .animation-delay-4 { animation-delay: 700ms; } .animation-delay-5 { animation-delay: 900ms; } 使用\n1 2 \u0026lt;h1 class=\u0026#34;animate__animated animate__bounce animation-delay-1\u0026#34;\u0026gt;An animated element\u0026lt;/h1\u0026gt; \u0026lt;h1 class=\u0026#34;animate__animated animate__bounce animation-delay-2\u0026#34;\u0026gt;Another animated element\u0026lt;/h1\u0026gt; wow.js 下载 1 pnpm add wow.js -D 引入 在vue3项目的main.ts中引入,内容如下:\n1 2 3 4 5 6 7 8 9 import WOW from \u0026#39;wow.js\u0026#39; new WOW({ boxClass: \u0026#39;wow\u0026#39;, // 类名,在用户滚动时显示隐藏的框。 animateClass: \u0026#39;animate__animated\u0026#39;, // 触发CSS动画的类名称 offset: 300, // 定义浏览器视口底部与隐藏框顶部之间的距离。当用户滚动并到达此距离时,隐藏的框会显示出来。 mobile: true, // 在移动设备上打开/关闭WOW.js。 live: true, // 在页面上同时检查新的WOW元素。 }).init() 使用 使用wow直接替代animate__animated即可\n1 \u0026lt;h1 class=\u0026#34;wow animate__bounce animation-delay-1\u0026#34;\u0026gt;An animated element\u0026lt;/h1\u0026gt; 结语 由于写一个页面需要使用到wow,好多年没用过了,查了一下文档超多版本教程,使用起来各种不成功,难受\u0026hellip;暂时也没找到可替代的方案\u0026hellip;\n参考文档 wow animate css ","date":"2023-01-28T16:09:44Z","permalink":"https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/","title":"wow.js和animate css在vue3中的应用"},{"content":"socket tcp server 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import socket def socket_tcp_server(server_ip: str = \u0026#39;0.0.0.0\u0026#39;, server_port: int = 9000, buffer_size: int = 1024): \u0026#34;\u0026#34;\u0026#34; socket tcp 服务端 :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 :param server_port: 服务器tcp server接收信息的端口, 默认9000 :param buffer_size: 套接字缓冲区大小, 默认1024 :return: none \u0026#34;\u0026#34;\u0026#34; tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_socket.bind((server_ip, server_port)) tcp_socket.listen(128) print(\u0026#39;服务端开始运行...\\n\u0026#39;) while True: client, sender_info = tcp_socket.accept() receive_data = client.recv(buffer_size) print(\u0026#39;客户端地址: {}\u0026#39;.format(sender_info)) print(\u0026#39;来自客户端的信息: {}\u0026#39;.format(receive_data.decode(\u0026#39;utf-8\u0026#39;))) # 返回消息 client.send(str.encode(\u0026#39;response...\u0026#39;)) if __name__ == \u0026#39;__main__\u0026#39;: socket_tcp_server() socket tcp client 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import socket def socket_tcp_client_send_message(message: str, server_ip: str, server_port: int, buffer_size: int = 1024): \u0026#34;\u0026#34;\u0026#34; socket tcp 客户端发送消息 :param message: 消息 :param server_ip: 服务端的ip地址 :param server_port: 服务端的端口号 :return: none \u0026#34;\u0026#34;\u0026#34; tcp_client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) tcp_client_socket.connect((server_ip, server_port)) tcp_client_socket.send(str.encode(message)) response = tcp_client_socket.recv(buffer_size) print(\u0026#39;response : {}\u0026#39;.format(response.decode())) tcp_client_socket.close() if __name__ == \u0026#39;__main__\u0026#39;: socket_tcp_client_send_message(\u0026#39;hello,world!\u0026#39;, \u0026#39;127.0.0.1\u0026#39;, 9000) ","date":"2022-06-09T22:57:54Z","permalink":"https://blog.hunterji.com/p/python3-socket-tcp-example/","title":"python3 socket tcp example"},{"content":"socket udp server 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import socket def socket_udp_server(server_ip: str = \u0026#39;0.0.0.0\u0026#39;, server_port: int = 9000, buffer_size: int = 1024): \u0026#34;\u0026#34;\u0026#34; socket udp 服务端 :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 :param server_port: 服务器udp server接收信息的端口, 默认9000 :param buffer_size: 套接字缓冲区大小, 默认1024 :return: none \u0026#34;\u0026#34;\u0026#34; udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_socket.bind((server_ip, server_port)) print(\u0026#39;服务端开始运行...\\n\u0026#39;) while True: receive_data, sender_info = udp_socket.recvfrom(buffer_size) print(\u0026#39;客户端地址: {}\u0026#39;.format(sender_info)) print(\u0026#39;来自客户端的信息: {}\u0026#39;.format(receive_data.decode(\u0026#39;utf-8\u0026#39;))) if __name__ == \u0026#39;__main__\u0026#39;: socket_udp_server() socket udp client 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import socket def socket_udp_client_send_message(message: str, server_ip: str, server_port: int): \u0026#34;\u0026#34;\u0026#34; socket udp 客户端发送消息 :param message: 消息 :param server_ip: 服务端的ip地址 :param server_port: 服务端的端口号 :return: none \u0026#34;\u0026#34;\u0026#34; udp_client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) udp_client_socket.sendto(str.encode(message), (server_ip, server_port)) if __name__ == \u0026#39;__main__\u0026#39;: socket_udp_client_send_message(\u0026#39;hello,world!\u0026#39;, \u0026#39;127.0.0.1\u0026#39;, 9000) ","date":"2022-06-09T22:57:54Z","permalink":"https://blog.hunterji.com/p/python3-socket-udp-example/","title":"python3 socket udp example"},{"content":"前言 基于架构的调整,前端开始转为微前端。经过调研,决定使用qiankun微服务框架来使用,本文将介绍VUE3+TS+qiankun的实践经过。微服务架构的优势之一在于可以结合不同技术栈的节点,基于技术栈的考虑,此处用的都是vue3。\n源码:https://github.com/Kuari/Blog/tree/master/Examples/microFrontend\n环境 vue 3.0.0 TypeScript 4.1.5 vue router 4.0.0 @vue/cli 4.5.15 qiankun 2.6.3 实践 架构 如上图所示,微服务架构将会由多个节点构成,首先由一个主节点site_base连接所有子节点,子节点可以不断拓展。\n主节点 主节点源码可见于https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base\n创建主节点,选择vue3+ts\n1 2 vue create site_base cd site_base 安装qiankun\n1 yarn add qiankun 在src/App.vue中添加路由和渲染节点\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \u0026lt;template\u0026gt; \u0026lt;div id=\u0026#34;nav\u0026#34;\u0026gt; \u0026lt;router-link to=\u0026#34;/\u0026#34;\u0026gt;Home\u0026lt;/router-link\u0026gt; | \u0026lt;router-link to=\u0026#34;/about\u0026#34;\u0026gt;About\u0026lt;/router-link\u0026gt; | \u0026lt;!-- 新增site1路由 --\u0026gt; \u0026lt;router-link to=\u0026#34;/site1\u0026#34;\u0026gt;Site1\u0026lt;/router-link\u0026gt; | \u0026lt;!-- 新增site2路由 --\u0026gt; \u0026lt;router-link to=\u0026#34;/site2\u0026#34;\u0026gt;Site2\u0026lt;/router-link\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;router-view/\u0026gt; \u0026lt;!-- 新增site1渲染节点 --\u0026gt; \u0026lt;div id=\u0026#34;site1\u0026#34; /\u0026gt; \u0026lt;!-- 新增site2渲染节点 --\u0026gt; \u0026lt;div id=\u0026#34;site2\u0026#34; /\u0026gt; \u0026lt;/template\u0026gt; 在src/main.ts中引入子节点配置\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import { createApp } from \u0026#39;vue\u0026#39; import App from \u0026#39;./App.vue\u0026#39; import router from \u0026#39;./router\u0026#39; import store from \u0026#39;./store\u0026#39; import { registerMicroApps, start } from \u0026#39;qiankun\u0026#39; const apps: any[] = [ { name: \u0026#39;site1\u0026#39;, // 应用的名字 entry: \u0026#39;http://localhost:9001/\u0026#39;, // 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) container: \u0026#39;#site1\u0026#39;, // 要渲染到的节点id,对应上一步中src/App.vue中的渲染节点 activeRule: \u0026#39;/site1\u0026#39; // 访问子节点路由 }, { name: \u0026#39;site2\u0026#39;, entry: \u0026#39;http://localhost:9002/\u0026#39;, container: \u0026#39;#site2\u0026#39;, activeRule: \u0026#39;/site2\u0026#39; } ] registerMicroApps(apps) // 注册应用 start() // 开启应用 createApp(App).use(store).use(router).mount(\u0026#39;#app\u0026#39;) 子节点 子节点源码可见于https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1\n此处以site1为例,site2同理。\n创建子节点,选择vue3+ts\n1 2 vue create site_1 cd site_1 编辑src/App.vue\n1 2 3 \u0026lt;template\u0026gt; \u0026lt;router-view /\u0026gt; \u0026lt;/template\u0026gt; 编辑src/views/Home.vue,修改其内容,写一点标识性的文本\n1 2 3 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt;Hello, Site1!\u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; 创建文件src/pulic-path.ts,第一行的注视一定要加,避免eslint对于变量的报错\n1 2 3 4 /* eslint-disable camelcase */ if ((window as any).__POWERED_BY_QIANKUN__) { __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__ } 编辑src/router/index.ts,此处直接返回routes,而不是router,并且修改\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { RouteRecordRaw } from \u0026#39;vue-router\u0026#39; const routes: Array\u0026lt;RouteRecordRaw\u0026gt; = [ { path: \u0026#39;/\u0026#39;, name: \u0026#39;Home\u0026#39;, component: () =\u0026gt; import(\u0026#39;../views/Home.vue\u0026#39;) }, { path: \u0026#39;/about\u0026#39;, name: \u0026#39;About\u0026#39;, component: () =\u0026gt; import(\u0026#39;../views/About.vue\u0026#39;) } ] // 直接返回routes,由其它地方处理创建路由 export default routes 编辑src/main.ts\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import \u0026#39;./public-path\u0026#39; import { createApp } from \u0026#39;vue\u0026#39; import { createRouter, createWebHistory } from \u0026#39;vue-router\u0026#39; import App from \u0026#39;./App.vue\u0026#39; import routes from \u0026#39;./router\u0026#39; import store from \u0026#39;./store\u0026#39; let router = null let instance: any = null let history: any = null function render (props = {}) { const { container } = props // 当为微服务主节点情况下访问,会设置二级路径,而直接访问时没有二级路径,此处需要根据实际情况修改 history = createWebHistory(window.__POWERED_BY_QIANKUN__ ? \u0026#39;/site1\u0026#39; : \u0026#39;/\u0026#39;) router = createRouter({ history, routes }) instance = createApp(App) instance.use(router) instance.use(store) instance.mount(container ? container.querySelector(\u0026#39;#app\u0026#39;) : \u0026#39;#app\u0026#39;) } if (!window.__POWERED_BY_QIANKUN__) { render() } export const bootstrap = async (): Promise\u0026lt;void\u0026gt; =\u0026gt; { console.log(\u0026#39;%c \u0026#39;, \u0026#39;color: green \u0026#39;, \u0026#39;vue3.0 app bootstraped\u0026#39;) } const storeTest = (props: any): void =\u0026gt; { props.onGlobalStateChange \u0026amp;\u0026amp; props.onGlobalStateChange( (value, prev) =\u0026gt; console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true ) props.setGlobalState \u0026amp;\u0026amp; props.setGlobalState({ ignore: props.name, user: { name: props.name } }) } export const mount = async (props: any): Promise\u0026lt;void\u0026gt; =\u0026gt; { storeTest(props) render(props) instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange instance.config.globalProperties.$setGlobalState = props.setGlobalState } export const unmount = async (): Promise\u0026lt;void\u0026gt; =\u0026gt; { instance.unmount() instance._container.innerHTML = \u0026#39;\u0026#39; instance = null router = null history.destroy() } 创建文件vue.config.js\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 const path = require(\u0026#39;path\u0026#39;) const { name } = require(\u0026#39;./package\u0026#39;) function resolve (dir) { return path.join(__dirname, dir) } const port = 9001 module.exports = { outputDir: \u0026#39;dist\u0026#39;, assetsDir: \u0026#39;static\u0026#39;, filenameHashing: true, devServer: { hot: true, disableHostCheck: true, port, overlay: { warnings: false, errors: true }, headers: { \u0026#39;Access-Control-Allow-Origin\u0026#39;: \u0026#39;*\u0026#39; } }, // 自定义webpack配置 configureWebpack: { resolve: { alias: { \u0026#39;@\u0026#39;: resolve(\u0026#39;src\u0026#39;) } }, output: { // 把子应用打包成 umd 库格式 library: `${name}-[name]`, libraryTarget: \u0026#39;umd\u0026#39;, jsonpFunction: `webpackJsonp_${name}` } } } 验证 主节点和子节点分别独立运行,但是子节点的地址需要跟主节点配置中子节点对应的地址相同。\n在主节点上点击子节点的路由,即可在主节点上访问子节点的页面了!\n主节点优化 主节点除了如上配置,可以进行两项优化:\n模块化子节点配置 添加过渡状态,当加载子节点时窗口顶部出现加载进度条 优化后主节点源码可见于https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize\n模块化子节点配置 创建文件夹src/childNodes,然后创建文件src/childNodes/apps.ts\n1 2 3 4 5 6 7 8 9 10 const apps: any[] = [ { name: \u0026#39;site1\u0026#39;, // 应用的名字 entry: \u0026#39;http://localhost:9001/\u0026#39;, // 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) container: \u0026#39;#site1\u0026#39;, // 要渲染到的节点id activeRule: \u0026#39;/site1\u0026#39; // 访问子节点路由 } ] export default apps 创建文件src/childNodes/index.ts\n1 2 3 4 5 6 import { registerMicroApps, start } from \u0026#39;qiankun\u0026#39; import apps from \u0026#39;./apps\u0026#39; registerMicroApps(apps) export default start 编辑src/main.ts\n1 2 3 4 5 6 7 8 9 import { createApp } from \u0026#39;vue\u0026#39; import App from \u0026#39;./App.vue\u0026#39; import router from \u0026#39;./router\u0026#39; import store from \u0026#39;./store\u0026#39; import start from \u0026#39;./childNodes\u0026#39; start() // 开启应用 createApp(App).use(store).use(router).mount(\u0026#39;#app\u0026#39;) 过渡效果 此处的过渡效果采用NProgress库,先来安装一波\n1 yarn add nprogress 编辑src/childNodes/index.ts\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { addGlobalUncaughtErrorHandler, registerMicroApps, start } from \u0026#39;qiankun\u0026#39; import apps from \u0026#39;./apps\u0026#39; import NProgress from \u0026#39;nprogress\u0026#39; import \u0026#39;nprogress/nprogress.css\u0026#39; registerMicroApps(apps, { // qiankun 生命周期钩子 - 子节点加载前 beforeLoad: (app: any) =\u0026gt; { NProgress.start() // 开始进度条 return Promise.resolve() }, // qiankun 生命周期钩子 - 子节点挂载后 afterMount: (app: any) =\u0026gt; { NProgress.done() // 进度条结束 return Promise.resolve() } }) export default start 结语 qiankun框架确实挺不错的,配置也并不是复杂,但是唯一想吐槽的一点是对于ts的支持感觉不太好/狗头,或许是我写得不够好吧,后面会持续优化使用。\n参考文档 qiankun官方文档 vue3+ts+qiankun的微前端快速上手 ","date":"2022-02-16T15:07:56Z","permalink":"https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/","title":"VUE3+TS+微前端实践"},{"content":"前言 i18n是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。 在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。\nNode.js本身有一个i18n的包,但是为了更好地结合vue,此处我们使用的是vue-i18n。\n源码:https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo\n环境 Vue 3.0.0 TypeScript 4.5.4 Vue Cli 4.5.15 安装 使用vue cli安装,会自动生成文件且引入。\n1 vue add i18n 执行过程中将需要填写如下问题:\n1 2 3 4 ? The locale of project localization. [默认选项]en ? The fallback locale of project localization. [默认选项]en ? The directory where store localization messages of project. It\u0026#39;s stored under `src` directory. [默认选项]locales ? Enable legacy API (compatible vue-i18n@v8.x) mode ? [默认选项]No 执行完成之后,将会自动处理如下文件:\n1 2 3 4 5 6 7 8 9 10 // vue add i18n执行结束后的git status new file: .env // 执行过程中的第一个和第二个问题将使用环境变量更新 modified: package.json new file: src/components/HelloI18n.vue // 官方给出的demo组件 new file: src/i18n.ts // 自动读取多语言的json文件且创建i18n实例 new file: src/locales/en.json // 多语言json文件所在文件夹,默认选择的en语言,所以生成一个默认的语言json文件 modified: src/main.ts // 引入i18n new file: vue.config.js // 配置i18n modified: yarn.lock 多语言配置 想要多语言则需要配置多个对应语言的json文件,其字段必须相同,否则当用户切换时,会出现找不到该字段对应文字的问题。\n为了便于切换和处理,此处不再使用en之类的简写,而是和系统语言名称对应起来,此处将使用zh_CN和en_GB来做配置。\n创建多语言json文件 此处生成两个文件src/locales/zh_CN.json和src/locales/en_GB.json,内容分别如下:\n1 2 3 4 { \u0026#34;language\u0026#34;: \u0026#34;中文\u0026#34;, \u0026#34;message\u0026#34;: \u0026#34;你好,世界!\u0026#34; } 1 2 3 4 { \u0026#34;language\u0026#34;: \u0026#34;English\u0026#34;, \u0026#34;message\u0026#34;: \u0026#34;Hello, World !\u0026#34; } 更新语言标识 将相关文件的en都改成en_GB。\n1 2 3 4 ##env VUE_APP_I18N_LOCALE=en_GB VUE_APP_I18N_FALLBACK_LOCALE=en_GB 1 2 3 4 5 6 7 8 9 // src/i18n.ts // ... export default createI18n({ legacy: false, locale: process.env.VUE_APP_I18N_LOCALE || \u0026#39;en_GB\u0026#39;, fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || \u0026#39;en_GB\u0026#39;, messages: loadLocaleMessages() }) 使用 重写首页 此处重写src/views/Home.vue文件,先按照正常的内容去写,内容如下:\n1 2 3 4 5 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div\u0026gt;Hello, World !\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; 配置多语言 需要配置多语言的地方就是展示的文字,所以将该文字替换掉即可。\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div\u0026gt;{{ t(\u0026#39;message\u0026#39;) }}\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script setup lang=\u0026#34;ts\u0026#34;\u0026gt; import { useI18n } from \u0026#39;vue-i18n\u0026#39; const { t } = useI18n() \u0026lt;/script\u0026gt; 此处引入t方法,而其参数message为上面配置的每一个json文件中的message字段,其将用json对应字段的value来展示。\n可能有小伙伴儿要问,那如果不是html中的展示文字怎么办呢?\n这也一样使用t方法的,示例如下:\n1 \u0026lt;input type=\u0026#34;text\u0026#34; :placeholder=\u0026#34;t(\u0026#39;message\u0026#39;)\u0026#34; /\u0026gt; 1 2 3 4 5 6 7 \u0026lt;script setup lang=\u0026#34;ts\u0026#34;\u0026gt; import { useI18n } from \u0026#39;vue-i18n\u0026#39; const { t } = useI18n() const exampleVal = t(\u0026#39;message\u0026#39;) \u0026lt;/script\u0026gt; 此处我们的json结构都是单层的,如果是嵌套的结构的话,可以使用t('home.message')这样的方式来引用。\n切换语言 此处需要引入locale,主要通过更新locale.value来切换语言。此处的切换是全局的,所以只需要写一个切换组件,其它组件都将会被切换语言。\n写一个下拉框来实现语言的切换,完整内容如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div\u0026gt;{{ t(\u0026#39;message\u0026#39;) }}\u0026lt;/div\u0026gt; \u0026lt;select v-model=\u0026#34;state.language\u0026#34; @change=\u0026#34;handleLanguageChange\u0026#34;\u0026gt; \u0026lt;option value=\u0026#34;zh_CN\u0026#34;\u0026gt;zh_CN\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026#34;en_GB\u0026#34;\u0026gt;en_GB\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script setup lang=\u0026#34;ts\u0026#34;\u0026gt; import { reactive } from \u0026#39;vue\u0026#39; import { useI18n } from \u0026#39;vue-i18n\u0026#39; const { t, locale } = useI18n() const state: { language: string } = reactive({ language: \u0026#39;en_GB\u0026#39; }) const handleLanguageChange = (option: { target: { value: string } }) =\u0026gt; { // 主要通过更新locale.value来切换语言 locale.value = option.target.value } \u0026lt;/script\u0026gt; 通过下拉框切换选项可以切换语言。\n本地环境 虽然i18n设置了默认的语言,但是友好的交互应当是根据用户的环境语言加载。这里需要使用navigator.language来拿到用户的环境语言,通过判断环境语言切换初始的语言配置。\n1 2 3 4 5 6 7 8 9 10 11 12 13 switch (navigator.language) { case \u0026#39;zh-CN\u0026#39;: state.language = \u0026#39;zh_CN\u0026#39; locale.value = \u0026#39;zh_CN\u0026#39; break case \u0026#39;en-GB\u0026#39;: state.language = \u0026#39;en_GB\u0026#39; locale.value = \u0026#39;en_GB\u0026#39; break default: state.language = \u0026#39;en_GB\u0026#39; locale.value = \u0026#39;en_GB\u0026#39; } 完整代码如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div\u0026gt;{{ t(\u0026#39;message\u0026#39;) }}\u0026lt;/div\u0026gt; \u0026lt;select v-model=\u0026#34;state.language\u0026#34; @change=\u0026#34;handleLanguageChange\u0026#34;\u0026gt; \u0026lt;option value=\u0026#34;zh_CN\u0026#34;\u0026gt;zh_CN\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026#34;en_GB\u0026#34;\u0026gt;en_GB\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script setup lang=\u0026#34;ts\u0026#34;\u0026gt; import { reactive, onMounted } from \u0026#39;vue\u0026#39; import { useI18n } from \u0026#39;vue-i18n\u0026#39; const { t, locale } = useI18n() const state: { language: string } = reactive({ language: \u0026#39;en_GB\u0026#39; }) const handleLanguageChange = (option: { target: { value: string } }) =\u0026gt; { // 主要通过更新locale.value来切换语言 locale.value = option.target.value } const fetchData = () =\u0026gt; { switch (navigator.language) { // 环境语言中间的线是居中的 case \u0026#39;zh-CN\u0026#39;: state.language = \u0026#39;zh_CN\u0026#39; locale.value = \u0026#39;zh_CN\u0026#39; break case \u0026#39;en-GB\u0026#39;: state.language = \u0026#39;en_GB\u0026#39; locale.value = \u0026#39;en_GB\u0026#39; break default: state.language = \u0026#39;en_GB\u0026#39; locale.value = \u0026#39;en_GB\u0026#39; } } onMounted(() =\u0026gt; { fetchData() }) \u0026lt;/script\u0026gt; 运行之后,默认语言不再是英语,而是跟环境语言一致的。\n参考文档 vue-i18n ","date":"2021-12-29T14:24:30Z","permalink":"https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/","title":"VUE项目国际化"},{"content":"报错 在使用vue cli plugin electron builder开发项目,用到涉及node相关的功能,比如fs、path,出现报错__dirname not defined 。\n解决 该问题在于需要开启electron对于node操作的支持。\n安装@types/node 1 yarn add @types/node -D 修改配置 修改electron的配置文件,此处我的配置文件为src/background.ts\n1 2 3 4 5 webPreferences: { // ... nodeIntegration: true, nodeIntegrationInWorker: true } ","date":"2021-11-29T17:29:14Z","permalink":"https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/","title":"electron的__dirname not defined报错解决"},{"content":"报错 在使用vue3+typescript+electron开发时,遇到一个报错为:\n1 Uncaught ReferenceError: require is not defined 点进去是module.exports = require(\u0026quot;events\u0026quot;),并不是自己的代码中的require,因此无法改变写法只能让项目去支持它。\n解决 在electron的配置文件中,新增或者修改如下配置:\n1 2 3 4 5 webPreferences: { // ... contextIsolation: false, nodeIntegration: true } 参考文档 Electron/Tedious: require(\u0026ldquo;events\u0026rdquo;) is not defined ","date":"2021-11-29T17:23:46Z","permalink":"https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/","title":"vue3+ts+electron不支持require is not defined报错解决"},{"content":"前言 最近在用uniapp开发小程序,需要用到canvas画海报然后再保存本地。\n之前写过同样功能的文章,不过场景不同,之前是在web上生成海报,该场景可以使用之前文章的方法——html转canvas来实现。\n但是uniapp则不同,该框架是去DOM化的,因此只能使用uniapp的官方canvas来实现。\n功能拆解 网上找到的文章,有几篇写得挺好的,展现了完整的功能。这里我把用到的几个功能拆解出来,而不用先通读整篇代码。\n创建canvas 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 \u0026lt;template\u0026gt; \u0026lt;view\u0026gt; \u0026lt;canvas style=\u0026#34;width: 300px; height: 200px;\u0026#34; canvas-id=\u0026#34;firstCanvas\u0026#34; id=\u0026#34;firstCanvas\u0026#34;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/view\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; export default { onReady() { // 初始化 const ctx = uni.createCanvasContext(\u0026#39;firstCanvas\u0026#39;) // 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中 ctx.draw() } } \u0026lt;/script\u0026gt; 背景色 1 2 3 ctx.setFillStyle(\u0026#39;red\u0026#39;) ctx.fillRect(0, 0, 300, 200) // 此处其实绘制了一个300x200的红色矩形,但是其大小跟canvas大小相同即为背景色了 加载图片 1)本地图片 图片直接在项目中,可以直接加载。\n1 ctx.drawImage(\u0026#34;./background.png\u0026#34;, 0, 0, 300, 200) 2)url 此处url返回的为文件流,在uniapp中无法直接加载,需要转换成本地信息才可以使用。\n有两种方式可以使用,小程序都需要添加download合法域名:\nuni.getImageInfo:获取文件信息,我使用的这个方法 uni.downloadFile:下载文件 原生使用方法是这样的:\n1 2 3 4 5 6 uni.getImageInfo({ src: url, success: (res) =\u0026gt; { ctx.drawImage(res.path, 0, 0, 300, 200) } }) 由于js的异步问题,如果图片较大或者多个图片的情况下,会有这边还没加载完,canvas就已经绘制完了的情况,所以这里将其优化下。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 首先封装 const getImageInfo = (url: string): Promise\u0026lt;string\u0026gt; =\u0026gt; { return new Promise((req, rej) =\u0026gt; { uni.getImageInfo({ src: url, success: (res) =\u0026gt; { req(res.path) } }) }) } // 调用 const genPoster = async() =\u0026gt; { const imgPath = await getImageInfo(\u0026lt;your-url\u0026gt;) // 建议所有图片在开始绘制canvas前加载好 const ctx = uni.createCanvasContext(\u0026#39;firstCanvas\u0026#39;) ctx.drawImage(imgPath, 0, 0, 300, 200) ctx.draw() } 3)base64 如果你的图片数据是base64的,那恭喜你,依然加载不了。当然这存在的情况是,微信开发工具是没有问题的,但是上了真机之后直接无法加载了,这波是小程序的锅。\n这里呢需要将图片存储然后用本地地址绘制。\n原生方法:\n1 2 3 4 5 6 7 8 9 10 11 12 13 const fs = wx.getFileSystemManager() let times = new Date().getTime() let codeImg = wx.env.USER_DATA_PATH + \u0026#39;/\u0026#39; + times + \u0026#39;.png\u0026#39; return new Promise((req, rej) =\u0026gt; { fs.writeFile({ filePath: imgPath, data: \u0026lt;your-base64-data\u0026gt;, encoding: \u0026#39;base64\u0026#39;, success: () =\u0026gt; { ctx.drawImage(imgPath, 0, 0, 300, 200) } }) }) 优化一波:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // 封装 const getBase64ImageInfo = (base64Data: string): Promise\u0026lt;string\u0026gt; =\u0026gt; { const fs = wx.getFileSystemManager() let times = new Date().getTime() let codeImg = wx.env.USER_DATA_PATH + \u0026#39;/\u0026#39; + times + \u0026#39;.png\u0026#39; return new Promise((req, rej) =\u0026gt; { fs.writeFile({ filePath: imgPath, data: base64Data, encoding: \u0026#39;base64\u0026#39;, success: () =\u0026gt; { req(imgPath) } }) }) } // 调用 const genPoster = async() =\u0026gt; { const imgPath = await getBase64ImageInfo(\u0026lt;your-base64-data\u0026gt;) // 建议所有图片在开始绘制canvas前加载好 const ctx = uni.createCanvasContext(\u0026#39;firstCanvas\u0026#39;) ctx.drawImage(imgPath, 0, 0, 300, 200) ctx.draw() } 文字 1 2 3 4 ctx.setFontSize(13) ctx.font = \u0026#34;nomarl bold 13px Arial,sans-serif\u0026#34; // 加粗等功能 ctx.setFillStyle(\u0026#39;#ffffff\u0026#39;) ctx.fillText(\u0026#34;hello, world !\u0026#34;, 16, 16) 圆角矩形 想要绘制一个圆角的矩形,啊\u0026hellip;\u0026hellip;这波就复杂了,原理就不细讲了,直接上代码,调用即可。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const roundedRect = (x: number, y: number, width: number, height: number, radius: number) =\u0026gt; { if (width \u0026lt;= 0 || height \u0026lt;= 0) { ctx.arc(x, y, radius, 0, Math.PI * 2); return; } ctx.moveTo(x + radius, y); ctx.arcTo(x + width, y, x + width, y + height, radius); ctx.arcTo(x + width, y + height, x, y + height, radius); ctx.arcTo(x, y + height, x, y, radius); ctx.arcTo(x, y, x + radius, y, radius); } const drawRoundedRect = (strokeStyle: string, fillStyle: string, x: number, y: number, width: number, height: number, radius: number) =\u0026gt; { ctx.beginPath(); roundedRect(x, y, width, height, radius); ctx.strokeStyle = strokeStyle; ctx.fillStyle = fillStyle; ctx.stroke(); ctx.fill(); } // 调用 drawRoundedRect(\u0026#39;#ffffff\u0026#39;, \u0026#39;#ffffff\u0026#39;, 16, 16, 86, 6) 图片加载为圆形 基本原理是,正常加载图片,canvas画个圆给它裁剪掉,上代码!\n1 2 3 4 5 6 7 8 9 ctx.save() ctx.beginPath() ctx.arc(16, 16, 12, 0, 2 * Math.PI) // 如果小伙伴儿调试时候感觉圆形和图片有点错位,可以开启下面两行注释代码,给圆圈加个边框 // ctx.setStrokeStyle(\u0026#39;#AAAAAA\u0026#39;) // ctx.stroke() ctx.clip() ctx.drawImage(\u0026lt;your-image-path\u0026gt;, 16, 16, 24, 24) ctx.restore() canvas生成的海报下载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const savePoster = () =\u0026gt; { uni.showModal({ title: \u0026#39;提示\u0026#39;, content: \u0026#39;确定保存到相册吗\u0026#39;, success: (response) =\u0026gt; { uni.canvasToTempFilePath({ canvasId: \u0026#39;sharePoster\u0026#39;, success: (response) =\u0026gt; { uni.saveImageToPhotosAlbum({ filePath: response.tempFilePath, success: (response) =\u0026gt; { console.log(response); // 此处为执行成功 // ... }, fail: (response) =\u0026gt; { uni.openSetting({ success: (response) =\u0026gt; { if (!response.authSetting[\u0026#39;scope.writePhotosAlbum\u0026#39;]) { uni.showModal({ title: \u0026#39;提示\u0026#39;, content: \u0026#39;获取权限成功,再次点击图片即可保存\u0026#39;, showCancel: false }) } else { uni.showModal({ title: \u0026#39;提示\u0026#39;, content: \u0026#39;获取权限失败,无法保存\u0026#39;, showCancel: false }) } } }) } }) }, fail: (response) =\u0026gt; { console.log(response); } }, this); } }) } 问题 图片有时显示有时不显示 1 参照本文“二.3.加载图片”优化代码处,将加载图片全部写成同步的,在开始绘制前将图片全都加载好。 base64数据的图片在小程序开发工具显示,到了真机就不显示了 1 参照本文“二.3.3)base64”优化代码处,使用该方法即可。小程序canvas无法直接加载base64图片。 canvas整体画成圆角的 1 canvas背景是透明色,只要画个大小覆盖canvas的圆角矩形或者使用圆角背景图即可。 ","date":"2021-11-25T17:07:55Z","permalink":"https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/","title":"uniapp canvas生成海报功能拆解和问题记录"},{"content":"问题 功能开发过程中写遮罩时,遇到遮罩下页面还可以滚动的问题。\n解决 直接给遮罩下的元素套上一个样式,使其不可滚动。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 \u0026lt;template\u0026gt; \u0026lt;div :class=\u0026#34;isPopup ? \u0026#39;disableRoll\u0026#39; : \u0026#39;\u0026#39;\u0026#34;\u0026gt; \u0026lt;div\u0026gt; ... \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; export default { data() { return { isPopup: false, } } } \u0026lt;/script\u0026gt; \u0026lt;style\u0026gt; .disableRoll { overflow: hidden; position: fixed; height: 100%; width: 100%; } \u0026lt;/style\u0026gt; ","date":"2021-11-25T16:13:10Z","permalink":"https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/","title":"vue禁止遮罩层下的页面滚动"},{"content":"前言 最近空下来,正好找个项目尝鲜,把vue3+ts+setup哐哐全堆上,试试最新的前端技术。\n从最先体会到的变化,就是关于响应式APIs了。遇到不好问题,怪我没有理解文档/狗头。比如说:\n明明这个数据改了,怎么没渲染出来? 同样是Arrary,怎么套了个reactive就类型不一样了? 所以这里基于遇到的几个问题,来写个笔记。\n简单对比 响应式官方文档 官方案例如下:\n1 2 3 4 5 6 7 8 9 \u0026lt;script setup\u0026gt; import { ref } from \u0026#39;vue\u0026#39; const count = ref(0) \u0026lt;/script\u0026gt; \u0026lt;template\u0026gt; \u0026lt;button @click=\u0026#34;count++\u0026#34;\u0026gt;{{ count }}\u0026lt;/button\u0026gt; \u0026lt;/template\u0026gt; 我们可以先看看,如果是vue2,怎么做呢?\n1 2 3 4 5 6 7 8 9 10 11 12 13 \u0026lt;script\u0026gt; export default { data() { return { count: 0 } } } \u0026lt;/script\u0026gt; \u0026lt;template\u0026gt; \u0026lt;button @click=\u0026#34;count++\u0026#34;\u0026gt;{{ count }}\u0026lt;/button\u0026gt; \u0026lt;/template\u0026gt; 可以很明显地看到此处的count跟上面的官方文档不同,使用了ref方法。这就是setup中的响应式APIs,需要预先声明响应式变量。\n如果,不声明呢?那就是直接写成如下:\n1 const count = 0 运行一下,首先你会发现,没有任何报错,代码正常运行。但是当你在浏览器上查看,开始改变count的值时,就会发现,怎么页面没有变化?\n所以,需要手动命名响应,才会在值变化时触发视图渲染。\nref 现在来详细讲讲ref,该方法常用于单个变量,比如:\n1 2 ref(0) ref(\u0026#34;hello\u0026#34;) 这里需要说明一个问题,那就是ref(\u0026quot;hello\u0026quot;) !== \u0026quot;hello\u0026quot;。这就是我说的,为什么同样的值,类型就不同了。那是因为ref返回的是一个Proxy,而非原来的值。在视图中可直接使用,但是在js/ts中操作,需要使用.value来操作,如下所示:\n1 2 3 4 5 let name = ref(\u0026#34;kuari\u0026#34;) function changeName() { name.value = \u0026#34;tom\u0026#34; } reactive reactive不同于ref的点在于,其是“深层”的——它影响所有嵌套 property。也就是说,其可用在对象或者数组上。\n1 2 3 4 let form = reactive({ name: \u0026#34;kuari\u0026#34;, desc: \u0026#34;developer\u0026#34; }) 其返回类型也是Proxy,不同点在于,可以直接修改某一个元素的,如下所示:\n1 form.name = \u0026#34;tom\u0026#34; 但是如果你想整个替换就会报错了。\n1 form = {...} // 报错,类型不同 当使用的是数组时,如果想整个替换,可以将其写成对象,代码如下所示:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let selected = reactive({ arr: [ { label: \u0026#34;vue\u0026#34;, value: 0 }, { label: \u0026#34;typescript\u0026#34;, value: 1 } ] }) // 使用 console.log(selected.arr) // 整个替换 selected.arr = [...] 关于reactive跟ref一起使用,reactive 将解包所有深层的ref,同时维持 ref 的响应性。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const count = ref(1) const obj = reactive({ count }) // ref 会被解包 console.log(obj.count === count.value) // true // 它会更新 `obj.count` count.value++ console.log(count.value) // 2 console.log(obj.count) // 2 // 它也会更新 `count` ref obj.count++ console.log(obj.count) // 3 console.log(count.value) // 3 总结 setup总体来说,用起来真的会更加简洁,而响应式虽然好像比之前麻烦些了,但是一定层面上让开发对对于程序有了更深入的操控。墙裂推荐一波!后面再来详细讲讲对于新特性的体验。\n","date":"2021-11-04T20:48:09Z","permalink":"https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/","title":"vue3 script setup响应式初体验"},{"content":"前言 前端小伙伴儿们是不是经常遇到ui组件全局引入导致体积太大,按需引入导致不断手写会很麻烦。所以,当当!今天我们来让代码自己按需引入,解放前端小伙伴儿们的生产力,早日实现下班自由!(甲方:我要再改十个需求!)\n介绍 我们这里介绍的是unplugin-vue-components。该组件是由vue核心开发成员antfu开发的,尤大也是推荐的,且是该项目的金牌赞助商。\n该组件主要是为了实现vue项目的组件自动引入。\n官方文档:antfu/unplugin-vue-components\n完整案例 此处我们以vite为例,来主要看一下其对于自定义组件和UI库组件的自动按需引入。\n源码:https://github.com/Kuari/Blog/tree/master/Examples/unplugin_auto_import\n创建项目 1 yarn create vite unplugin_auto_import --template vue 然后进入文件夹安装依赖。\n1 2 cd unplugin_auto_import yarn install 安装unplugin-vue-components 1 yarn add -D unplugin-vue-components 配置vite.config.js 1 2 3 4 5 6 7 8 9 10 11 import { defineConfig } from \u0026#39;vite\u0026#39; import vue from \u0026#39;@vitejs/plugin-vue\u0026#39; import Components from \u0026#39;unplugin-vue-components/vite\u0026#39; // 新增 // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), Components({ /* options */ }) // 新增 ] }) 自动引入自定义组件 我们默认模板创建的项目中,默认在App.vue中引入了./components/HelloWorld.vue。此处就可以来尝试下如何自动引入了。\n在配置了unplugin-vue-components之后,现在只需要删除引入行(其实这时候打开vscode就会发现改行已经灰掉了),被删除行如下所示:\n1 import HelloWorld from \u0026#39;./components/HelloWorld.vue\u0026#39; 删除以后完整App.vue如下所示:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;script setup\u0026gt; \u0026lt;/script\u0026gt; \u0026lt;template\u0026gt; \u0026lt;img alt=\u0026#34;Vue logo\u0026#34; src=\u0026#34;./assets/logo.png\u0026#34; /\u0026gt; \u0026lt;HelloWorld msg=\u0026#34;Hello Vue 3 + Vite\u0026#34; /\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;style\u0026gt; #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } \u0026lt;/style\u0026gt; 现在在命令行中运行yarn dev,再打开浏览器查看http://localhost:3000页面,是不是发现,哎?!居然引入了!(心中狂喜,要早日实现下班自由了)\n自动引入UI库组件 这里以element plus为例。\n首先是安装element plus。官方文档也没说要装,我还以为内置呢,一直报错,有点懵逼,哈哈。\n1 yarn add -D element-plus 引入element plus的resolver,此处编辑vite.config.js文件,修改后如下所示:\n1 2 3 4 5 6 7 8 9 10 11 12 import { defineConfig } from \u0026#39;vite\u0026#39; import vue from \u0026#39;@vitejs/plugin-vue\u0026#39; import Components from \u0026#39;unplugin-vue-components/vite\u0026#39; import { ElementPlusResolver } from \u0026#39;unplugin-vue-components/resolvers\u0026#39; // 引入ElementPlusResolver // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), Components({resolvers: [ElementPlusResolver()]}) // 添加配置 ] }) 那么现在神奇的事情来了,就可以直接使用UI库的组件了!\n我们在App.vue中使用一个el-button组件试试。我们在App.vue中加入如下行:\n1 \u0026lt;el-button type=\u0026#34;primary\u0026#34;\u0026gt;Kuari\u0026lt;/el-button\u0026gt; 添加后App.vue上下代码如下:\n1 2 3 4 5 \u0026lt;template\u0026gt; \u0026lt;img alt=\u0026#34;Vue logo\u0026#34; src=\u0026#34;./assets/logo.png\u0026#34; /\u0026gt; \u0026lt;HelloWorld msg=\u0026#34;Hello Vue 3 + Vite\u0026#34; /\u0026gt; \u0026lt;el-button type=\u0026#34;primary\u0026#34;\u0026gt;Kuari\u0026lt;/el-button\u0026gt; \u0026lt;/template\u0026gt; 现在,运行yarn dev,打开浏览器,可以看到,就直接可以使用UI库的组件了。\n打包 按需引入的功能并不是仅仅在开发时候的,在打包时,该组件也是Tree-shakable的,只会将你用了的组件打包。\n按照当前教程所写的项目,当全局引入的时候,打包的dist文件夹为1.1MB,而使用该组件之后打包,其dist文件夹为202KB。\n最后 手动按需导入是不可能手动按需导入的,这辈子都不可能了/狗头。\n毕竟是大佬开发的强力工具,希望前端小伙伴儿们早日实现下班自由。\n","date":"2021-10-27T15:28:00Z","permalink":"https://blog.hunterji.com/p/%E4%BD%A0%E6%98%AF%E4%B8%AA%E6%88%90%E7%86%9F%E7%9A%84%E4%BB%A3%E7%A0%81%E8%A6%81%E5%AD%A6%E4%BC%9A%E8%87%AA%E5%B7%B1%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5%E4%BA%86/","title":"你是个成熟的代码要学会自己按需引入了"},{"content":"简介 在上一篇文章Vite+Electron快速构建一个VUE3桌面应用中,我们了解了如何使用Vite和Electron来快速构建一个Vue3桌面应用。但是,之前构建的应用仅仅是一个简单的版本。在开发过程中,为了更好的开发体验,在开发electron的时候,肯定也希望能有动态模块热重载(HMR),更别说vite那迅雷不及掩耳盗铃儿响叮当之势的加载速度。\n因此,接着上一篇文章所完成的项目代码,我们来完成Vite和Electron开发时的动态模块热重载功能。\n源码:https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2\n系列文章:\nVite+Electron快速构建一个VUE3桌面应用 Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 Vite+Electron快速构建一个VUE3桌面应用(三)——打包 思路 先说结论,可利用electron中的mainWindow.loadURL(\u0026lt;your-url\u0026gt;)来实现。\n对于动态模块热重载功能来说,无论是webpack还是vite,其都是将构建内容存入内存,因此我们无法使用mainWindow.loadFile('dist/index.html')这样加载文件的方式。\n但是,单纯地改变该配置也是不行的,需要使用vite将开发服务器运行起来,可以正常运行动态模块热重载,而electron直接加载其开发服务器可访问的url,即http://localhost:3000。\n实现步骤 编辑main.js 将mainWindow.loadFile('dist/index.html')更新为mainWindow.loadURL(\u0026quot;http://localhost:3000\u0026quot;),更新后的文件如下所示:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 // main.js // 控制应用生命周期和创建原生浏览器窗口的模组 const { app, BrowserWindow } = require(\u0026#39;electron\u0026#39;) const path = require(\u0026#39;path\u0026#39;) function createWindow () { // 创建浏览器窗口 const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, \u0026#39;preload.js\u0026#39;) } }) // 加载 index.html // mainWindow.loadFile(\u0026#39;dist/index.html\u0026#39;) 将该行改为下面这一行,加载url mainWindow.loadURL(\u0026#34;http://localhost:3000\u0026#34;) // 打开开发工具 // mainWindow.webContents.openDevTools() } // 这段程序将会在 Electron 结束初始化 // 和创建浏览器窗口的时候调用 // 部分 API 在 ready 事件触发后才能使用。 app.whenReady().then(() =\u0026gt; { createWindow() app.on(\u0026#39;activate\u0026#39;, function () { // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 // 打开的窗口,那么程序会重新创建一个窗口。 if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) // 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 // 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 app.on(\u0026#39;window-all-closed\u0026#39;, function () { if (process.platform !== \u0026#39;darwin\u0026#39;) app.quit() }) // 在这个文件中,你可以包含应用程序剩余的所有部分的代码, // 也可以拆分成几个文件,然后用 require 导入。 编辑vite.config.js 修改文件vite.config.js的base,修改后的文件如下所示:\n1 2 3 4 5 6 7 8 9 10 // vite.config.js import { defineConfig } from \u0026#39;vite\u0026#39; import vue from \u0026#39;@vitejs/plugin-vue\u0026#39; // https://vitejs.dev/config/ export default defineConfig({ base: \u0026#34;./\u0026#34;,\t// 新增 plugins: [vue()] }) 同时开启vite和electron服务 为了使vite和electron正常运行,需要先运行vite,使得其开发服务器的url可以正常访问,然后再开启electron去加载url。\n此处需要安装两个库:\nconcurrently:阻塞运行多个命令,-k参数用来清除其它已经存在或者挂掉的进程 wait-on:等待资源,此处用来等待url可访问 首先来安装。\n1 yarn add -D concurrently wait-on 接着更新文件package.json,scripts新增两条命令:\n1 2 3 4 \u0026#34;scripts\u0026#34;: { \u0026#34;electron\u0026#34;: \u0026#34;wait-on tcp:3000 \u0026amp;\u0026amp; electron .\u0026#34;, \u0026#34;electron:serve\u0026#34;: \u0026#34;concurrently -k \\\u0026#34;yarn dev\\\u0026#34; \\\u0026#34;yarn electron\\\u0026#34;\u0026#34; }, 更新后完整内容如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 { \u0026#34;name\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;main.js\u0026#34;, \u0026#34;scripts\u0026#34;: { \u0026#34;dev\u0026#34;: \u0026#34;vite\u0026#34;, \u0026#34;build\u0026#34;: \u0026#34;vite build\u0026#34;, \u0026#34;serve\u0026#34;: \u0026#34;vite preview\u0026#34;, \u0026#34;electron\u0026#34;: \u0026#34;wait-on tcp:3000 \u0026amp;\u0026amp; electron .\u0026#34;, \u0026#34;electron:serve\u0026#34;: \u0026#34;concurrently -k \\\u0026#34;yarn dev\\\u0026#34; \\\u0026#34;yarn electron\\\u0026#34;\u0026#34; }, \u0026#34;dependencies\u0026#34;: { \u0026#34;vue\u0026#34;: \u0026#34;^3.2.16\u0026#34; }, \u0026#34;devDependencies\u0026#34;: { \u0026#34;@vitejs/plugin-vue\u0026#34;: \u0026#34;^1.9.3\u0026#34;, \u0026#34;concurrently\u0026#34;: \u0026#34;^6.3.0\u0026#34;, \u0026#34;cross-env\u0026#34;: \u0026#34;^7.0.3\u0026#34;, \u0026#34;electron\u0026#34;: \u0026#34;^15.1.2\u0026#34;, \u0026#34;vite\u0026#34;: \u0026#34;^2.6.4\u0026#34;, \u0026#34;wait-on\u0026#34;: \u0026#34;^6.0.0\u0026#34; } } 运行 现已添加两条命令:\nyarn electron为等待tcp协议3000端口可访问,然后执行electron yarn electron:serve为阻塞执行开发服务器运行和yarn electron命令 运行项目只要执行命令yarn electron:serve即可,当修改项目文件时,桌面应用也将自动更新。\n参考文件 为什么选vite vite+vue3+electron+typescript ","date":"2021-10-25T16:07:10Z","permalink":"https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/","title":"Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载"},{"content":"简介 关于测试工程师,有一个笑话,是这样的:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 一个测试工程师走进一家酒吧,要了一杯啤酒 一个测试工程师走进一家酒吧,要了一杯咖啡 一个测试工程师走进一家酒吧,要了0.7杯啤酒 一个测试工程师走进一家酒吧,要了-1杯啤酒 一个测试工程师走进一家酒吧,要了2^32杯啤酒 一个测试工程师走进一家酒吧,要了一杯洗脚水 一个测试工程师走进一家酒吧,要了一杯蜥蜴 一个测试工程师走进一家酒吧,要了一份asdfQwer@24dg!\u0026amp;*(@ 一个测试工程师走进一家酒吧,什么也没要 一个测试工程师走进家酒吧,又走出去又从窗户进来又从后门出去从下水道钻进来 一个测试工程师走进家酒吧,又走出去又进来又出去又进来又出去,最后在外面把老板打了一顿 一个测试工程师走进一 一个测试工程师走进一家酒吧,要了一杯烫烫烫的锟斤拷 一个测试工程师走进一家酒吧,要了NaN杯Null 1T测试工程师冲进一家酒吧,要了500T啤酒咖啡洗脚水野猫狼牙棒奶茶 1T测试工程师把酒吧拆了 一个测试工程师化装成老板走进一家酒吧,要了500杯啤酒,并且不付钱 一万个测试工程师在酒吧外呼啸而过 一个测试工程师走进一家酒吧,要了一杯啤酒‘;DROPTABLE酒吧 测试工程师们满意地离开了酒吧 这个笑话估计也只有开发才明白其中的笑点与心酸吧。\n对于一些刚入门的开发来说,这简直就是噩梦。当初刚入门的时候我的代码也是很多毛病,经不起这样的测试,后来渐渐地经验多了后,代码的健壮性逐渐提升,也明白其中比较重要的就是参数的校验。\n参数的使用有通过协议的接口调用(如http、rpc\u0026hellip;\u0026hellip;)、函数调用、库调用等等方式。\n其实对于http api的请求来说,现在很多web框架都已经自带了参数校验的功能,基本用起来都挺爽的,也无需多讲。\n而对于函数调用这样的常见方式,很多是要靠开发自己去校验参数的。如果仅仅是靠注释,在团队开发过程中,难免会有问题产生。起码我觉得,永远不要相信传过来的参数!\nOK,那么来讲讲这次的题目,就是Golang中的参数校验库——validator。\n用过Gin的小伙伴儿应该知道其binding参数验证器非常好用,其就是调用了validator。此处呢我们来介绍下validator的基础用法,和在一般场景下的应用案例。\n基础用法 介绍 validator包源码在github.com/go-playground/validator。其基于标记实现结构和单个字段的值验证,包含如下关键功能:\n使用验证标记或自定义验证程序进行跨字段和跨结构验证 Slice、Array和Map都可以允许验证多维字段的任何或者所有级别 能够深入查看映射键和值以进行验证 通过在验证之前确定类型接口的基础类型来处理类型接口 处理自定义字段类型 允许将多个验证映射到单个标记,以便在结构上更轻松地定义验证 提取自定义定义的字段名,例如,可以指定在验证时提取JSON名称,并使其在结果FieldError中可用 可定制的i18n错误消息 gin web框架的默认验证器 安装 1 go get github.com/go-playground/validator/v10 导入 1 import \u0026#34;github.com/go-playground/validator/v10\u0026#34; 验证规则 此处从官方列举的各个类别中挑选部分举例说明。\n1)比较 eq:相等 gt:大于 gte:大于等于 lt:小于 lte:小于等于 ne:不等于 2)字段 此处的字段大部分可以理解为上面的比较的tag跟field拼接而成,而中间有cs的tag为跨struct比较。\neqfield(=Field):必须等于Field的值 nefield(=Field):必须不等于Field的值 gtfield(=Field):必须大于Field的值 eqcsfield(=Other.Field):必须等于struct Other中的Field的值 3)网络 ip:网络协议地址IP ip4_addr:网络协议地址IPv4 mac:mac地址 url:url 4)字符 ascii:ASCII boolean:Boolean endswith: 以\u0026hellip;结尾 contains:包含 uppercase:大写 5)格式 base64:Base64字符串 base64url:Base64url字符串 email:邮箱字符串 json:JSON jwt:JSON Web Token latitude:纬度 6)其它 len:长度 max:最大值 min:最小值 required:字段为必须,不可空 7)别名 iscolor:hexcolor|rgb|rgba|hsl|hsla country_code:iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric 案例 简单验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;github.com/go-playground/validator/v10\u0026#34; ) type User struct { Name string `validate:\u0026#34;required,lte=10\u0026#34;` // 姓名 非空,长度小于等于10 Age int `validate:\u0026#34;required,gte=18,lte=50\u0026#34;` // 年龄 非空,数字大于等于18,小于等于50 Email string `validate:\u0026#34;required,email\u0026#34;` // 邮箱 非空,格式为email FavouriteColor string `validate:\u0026#34;iscolor\u0026#34;` // 喜欢的颜色 hexcolor|rgb|rgba|hsl|hsla的别名 Password string `validate:\u0026#34;required,gte=16,lte=22\u0026#34;` // 密码 非空,长度大于等于16,小于等于22 RePassword string `validate:\u0026#34;required,gte=16,lte=22,eqfield=Password\u0026#34;` // 确认密码 非空,长度大于等于16,小于等于22,必须和字段Password相同 Hobbies []Hobby `validate:\u0026#34;lte=5\u0026#34;` // 多个爱好 长度小于等于5 } type Hobby struct { Name string `validate:\u0026#34;lte=50\u0026#34;` // 爱好名称 长度小于等于50 } var validate *validator.Validate func main() { validate = validator.New() // 该函数验证struct // 不会报错 validateStruct() // 该函数单度验证字段 // 会报错 validateVariable() } func validateStruct() { hobby := Hobby{ Name: \u0026#34;划水\u0026#34;, } user := User{ Name: \u0026#34;张三\u0026#34;, Age: 48, Email: \u0026#34;hi.hunterji@gmail.com\u0026#34;, FavouriteColor: \u0026#34;#ffffff\u0026#34;, Password: \u0026#34;1234567890123456\u0026#34;, RePassword: \u0026#34;1234567890123456\u0026#34;, Hobbies: []Hobby{hobby}, } err := validate.Struct(user) if err != nil { fmt.Println(err) } } func validateVariable() { email := \u0026#34;hi.hunterji@gmail.com\u0026#34; // 此处邮箱地址格式写的是错误的,会导致报错 err := validate.Var(email, \u0026#34;required,email\u0026#34;) if err != nil { fmt.Println(err) } } 自定义验证 自定义验证可以自己创建一个校验的函数:\n1 2 3 4 // 注册校验函数 func ValidateMyVal(fl validator.FieldLevel) bool { return fl.Field().String() == \u0026#34;hello,world!\u0026#34; } 然后将其注册到validate上即可:\n1 2 3 4 5 6 7 8 validate = validator.New() validate.RegisterValidation(\u0026#34;is-hello\u0026#34;, ValidateMyVal) s := \u0026#34;hello,kuari\u0026#34; // 跟校验函数中的字符串不同,因此此处会报错 err := validate.Var(s, \u0026#34;is-hello\u0026#34;) if err != nil { fmt.Println(err) } 自定义校验可以满足开发过程中的特殊场景,通过制定规范的校验标准,可以推进团队的协作和开发效率。\n最后 至此便是对于validator的介绍了。本文篇幅较短,管中窥豹而已,基本可以满足简单场景的使用。以及本文的案例也是基于官方的案例改的,让其稍微接地气点。若有兴趣的小伙伴还是建议去完整看一下官方的文档和案例,多样的用法可以满足多样的场景,在满足代码的健壮性的同时也能确保代码的优美。\n参考文档 go-playground/validator ","date":"2021-10-23T15:31:19Z","permalink":"https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/","title":"Golang如何使用validator校验参数"},{"content":"简介 上一篇文章Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载完成了开发时的动态模块热重载功能,现在是时候来看看怎么完成最后一步——打包了。\n源码:https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3\n系列文章:\nVite+Electron快速构建一个VUE3桌面应用 Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 Vite+Electron快速构建一个VUE3桌面应用(三)——打包 思路 先说结论,重点还是在于mainWindow.loadURL()。\n打包后还是加载http://localhost:3000是无法运行的,因此,此处需要先用vite打包好,然后使用electron-builder加载vite打包后的文件进行打包。\n为了代码能够根据不同环境在运行时加载http://localhost:3000,在打包时加载文件,此处需要使用环境变量来切换生产和开发环境。\n实现 环境变量 此处使用环境变量NODE_ENV来切换生产和开发环境,生产环境为NODE_ENV=production,开发环境为NODE_ENV=development,若有其它如release等环境可在此基础上拓展。\n创建electron文件夹 在项目根目录下创建文件夹electron,将main.js和preload.js文件移动进来。其结构如下所示:\n1 2 3 4 5 6 . ├── README.md ├── electron │ ├── main.js │ └── preload.js ... 若还是不太明白可以看看源码中文件结构。\n编辑electron/main.js 该文件主要是需要根据环境变量切换electron加载的内容,修改内容如下:\n1 2 3 4 5 mainWindow.loadURL( NODE_ENV === \u0026#39;development\u0026#39; ? \u0026#39;http://localhost:3000\u0026#39; :`file://${path.join(__dirname, \u0026#39;../dist/index.html\u0026#39;)}` ); 修改后的完整内容如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 // electron/main.js // 控制应用生命周期和创建原生浏览器窗口的模组 const { app, BrowserWindow } = require(\u0026#39;electron\u0026#39;) const path = require(\u0026#39;path\u0026#39;) const NODE_ENV = process.env.NODE_ENV function createWindow () { // 创建浏览器窗口 const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, \u0026#39;preload.js\u0026#39;) } }) // 加载 index.html // mainWindow.loadFile(\u0026#39;dist/index.html\u0026#39;) 将该行改为下面这一行,加载url mainWindow.loadURL( NODE_ENV === \u0026#39;development\u0026#39; ? \u0026#39;http://localhost:3000\u0026#39; :`file://${path.join(__dirname, \u0026#39;../dist/index.html\u0026#39;)}` ); // 打开开发工具 if (NODE_ENV === \u0026#34;development\u0026#34;) { mainWindow.webContents.openDevTools() } } // 这段程序将会在 Electron 结束初始化 // 和创建浏览器窗口的时候调用 // 部分 API 在 ready 事件触发后才能使用。 app.whenReady().then(() =\u0026gt; { createWindow() app.on(\u0026#39;activate\u0026#39;, function () { // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 // 打开的窗口,那么程序会重新创建一个窗口。 if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) // 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 // 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 app.on(\u0026#39;window-all-closed\u0026#39;, function () { if (process.platform !== \u0026#39;darwin\u0026#39;) app.quit() }) // 在这个文件中,你可以包含应用程序剩余的所有部分的代码, // 也可以拆分成几个文件,然后用 require 导入。 编辑package.json 首先修改main 属性,将main: main.js改为main: electron/main.js。\n1 2 3 4 5 6 { \u0026#34;name\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;electron/main.js\u0026#34;, ... } 接着,编辑build属性:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \u0026#34;build\u0026#34;: { \u0026#34;appId\u0026#34;: \u0026#34;com.your-website.your-app\u0026#34;, \u0026#34;productName\u0026#34;: \u0026#34;ElectronApp\u0026#34;, \u0026#34;copyright\u0026#34;: \u0026#34;Copyright © 2021 \u0026lt;your-name\u0026gt;\u0026#34;, \u0026#34;mac\u0026#34;: { \u0026#34;category\u0026#34;: \u0026#34;public.app-category.utilities\u0026#34; }, \u0026#34;nsis\u0026#34;: { \u0026#34;oneClick\u0026#34;: false, \u0026#34;allowToChangeInstallationDirectory\u0026#34;: true }, \u0026#34;files\u0026#34;: [ \u0026#34;dist/**/*\u0026#34;, \u0026#34;electron/**/*\u0026#34; ], \u0026#34;directories\u0026#34;: { \u0026#34;buildResources\u0026#34;: \u0026#34;assets\u0026#34;, \u0026#34;output\u0026#34;: \u0026#34;dist_electron\u0026#34; } } 然后,更新scripts属性。\n此处需要先安装两个库:\ncross-env: 该库让开发者只需要注重环境变量的设置,而无需担心平台设置 electron-builder: electron打包库 1 yarn add -D cross-env electron-builder 更新后的scripts如下:\n1 2 3 4 5 6 7 8 { \u0026#34;dev\u0026#34;: \u0026#34;vite\u0026#34;, \u0026#34;build\u0026#34;: \u0026#34;vite build\u0026#34;, \u0026#34;serve\u0026#34;: \u0026#34;vite preview\u0026#34;, \u0026#34;electron\u0026#34;: \u0026#34;wait-on tcp:3000 \u0026amp;\u0026amp; cross-env NODE_ENV=development electron .\u0026#34;, // 此处需要设置环境变量以保证开发时加载url \u0026#34;electron:serve\u0026#34;: \u0026#34;concurrently -k \\\u0026#34;yarn dev\\\u0026#34; \\\u0026#34;yarn electron\\\u0026#34;\u0026#34;, \u0026#34;electron:build\u0026#34;: \u0026#34;vite build \u0026amp;\u0026amp; electron-builder\u0026#34; // 新增打包命令 } 最后,更新后的package.json完整内容如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 { \u0026#34;name\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;electron/main.js\u0026#34;, \u0026#34;scripts\u0026#34;: { \u0026#34;dev\u0026#34;: \u0026#34;vite\u0026#34;, \u0026#34;build\u0026#34;: \u0026#34;vite build\u0026#34;, \u0026#34;serve\u0026#34;: \u0026#34;vite preview\u0026#34;, \u0026#34;electron\u0026#34;: \u0026#34;wait-on tcp:3000 \u0026amp;\u0026amp; cross-env NODE_ENV=development electron .\u0026#34;, \u0026#34;electron:serve\u0026#34;: \u0026#34;concurrently -k \\\u0026#34;yarn dev\\\u0026#34; \\\u0026#34;yarn electron\\\u0026#34;\u0026#34;, \u0026#34;electron:build\u0026#34;: \u0026#34;vite build \u0026amp;\u0026amp; electron-builder\u0026#34; }, \u0026#34;dependencies\u0026#34;: { \u0026#34;vue\u0026#34;: \u0026#34;^3.2.16\u0026#34; }, \u0026#34;devDependencies\u0026#34;: { \u0026#34;@vitejs/plugin-vue\u0026#34;: \u0026#34;^1.9.3\u0026#34;, \u0026#34;concurrently\u0026#34;: \u0026#34;^6.3.0\u0026#34;, \u0026#34;cross-env\u0026#34;: \u0026#34;^7.0.3\u0026#34;, \u0026#34;electron\u0026#34;: \u0026#34;^15.1.2\u0026#34;, \u0026#34;electron-builder\u0026#34;: \u0026#34;^22.13.1\u0026#34;, \u0026#34;vite\u0026#34;: \u0026#34;^2.6.4\u0026#34;, \u0026#34;wait-on\u0026#34;: \u0026#34;^6.0.0\u0026#34; }, \u0026#34;build\u0026#34;: { \u0026#34;appId\u0026#34;: \u0026#34;com.my-website.my-app\u0026#34;, \u0026#34;productName\u0026#34;: \u0026#34;MyApp\u0026#34;, \u0026#34;copyright\u0026#34;: \u0026#34;Copyright © 2021 kuari\u0026#34;, \u0026#34;mac\u0026#34;: { \u0026#34;category\u0026#34;: \u0026#34;public.app-category.utilities\u0026#34; }, \u0026#34;nsis\u0026#34;: { \u0026#34;oneClick\u0026#34;: false, \u0026#34;allowToChangeInstallationDirectory\u0026#34;: true }, \u0026#34;files\u0026#34;: [ \u0026#34;dist/**/*\u0026#34;, \u0026#34;electron/**/*\u0026#34; ], \u0026#34;directories\u0026#34;: { \u0026#34;buildResources\u0026#34;: \u0026#34;assets\u0026#34;, \u0026#34;output\u0026#34;: \u0026#34;dist_electron\u0026#34; } } } 打包 直接执行打包命令即可开始打包。\n1 yarn electron:build 打包完成之后,会多出两个文件夹dist和dist_electron,其文件结构如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 . ├── README.md ├── dist │ ├── assets │ ├── favicon.ico │ └── index.html ├── dist_electron │ ├── MyApp-0.0.0-mac.zip │ ├── MyApp-0.0.0-mac.zip.blockmap │ ├── MyApp-0.0.0.dmg │ ├── MyApp-0.0.0.dmg.blockmap │ ├── builder-debug.yml │ ├── builder-effective-config.yaml │ └── mac ... 至此,便完成了打包。\n后面再来写写关于electron的优化,减少electron打包后应用的体积。(这玩意儿确实打包下来有点大呢/狗头)\n","date":"2021-10-19T15:26:46Z","permalink":"https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/","title":"Vite+Electron快速构建一个VUE3桌面应用(三)——打包"},{"content":"简介 首先,介绍下vite和Electron。\nVite是一种新型前端构建工具,能够显著提升前端开发体验。由尤大推出,其发动态表示“再也回不去webpack了\u0026hellip;” Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。 当开始想用vue去开发一个桌面应用时,首先去搜索下,了解到当前如下两种现成方案:\nelectron-vue: 该项目集成度较好,封装较为完整,中文搜索下来文章较多也是该方案,可以直接上手去使用。但是,问题在于其内置electron的版本太低,写文章时看到的版本是2.0.4,而最新的electron版本是15.1.2。 Vue CLI Plugin Electron Builder: 该方案是集成到到vue-cli中使用,使用vue add electron-builder后可直接上手,免去了基础配置的步骤。但是其只能在vue-cli下使用,无法配合vite来使用。 因此,若要使用vite和electron,还需要自己来配置。\n源码:https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1\n创建一个Vite项目 安装 vite 1 yarn create vite 创建项目 创建命令如下:\n1 yarn create vite \u0026lt;your-vue-app-name\u0026gt; --template vue 此处创建一个项目,名为kuari。\n1 yarn create vite kuari --template vue 进入且运行 进入项目,在运行前需要先安装下依赖。\n1 2 3 cd kuari yarn install yarn dev 在运行命令敲下的一瞬间,几乎是已经在运行了,不愧是vite。此时按照输出,打开地址预览,即可看到初始化页面。\n至此一个基础的vite项目创建完成。\n配置Electron 官方文档 在Electron官网的快速入门文档中,有官方给出的利用html、javascript、css来创建一个electron应用的案例,vite+electron的方案也借鉴其中。\n安装 首先安装electron至vite应用。目前electron的版本为^15.1.2,。\n1 yarn add --dev electron 配置文件 ####config.js\n1 2 3 4 5 6 7 8 9 import { defineConfig } from \u0026#39;vite\u0026#39; import vue from \u0026#39;@vitejs/plugin-vue\u0026#39; import path from \u0026#39;path\u0026#39; // 新增 // https://vitejs.dev/config/ export default defineConfig({ base: path.resolve(__dirname, \u0026#39;./dist/\u0026#39;),\t// 新增 plugins: [vue()] }) ####js\n创建一个新的文件main.js,需要注意的是,该内容中index.html的加载路径跟electron官网给的配置不同。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 // main.js // 控制应用生命周期和创建原生浏览器窗口的模组 const { app, BrowserWindow } = require(\u0026#39;electron\u0026#39;) const path = require(\u0026#39;path\u0026#39;) function createWindow () { // 创建浏览器窗口 const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, \u0026#39;preload.js\u0026#39;) } }) // 加载 index.html mainWindow.loadFile(\u0026#39;dist/index.html\u0026#39;) // 此处跟electron官网路径不同,需要注意 // 打开开发工具 // mainWindow.webContents.openDevTools() } // 这段程序将会在 Electron 结束初始化 // 和创建浏览器窗口的时候调用 // 部分 API 在 ready 事件触发后才能使用。 app.whenReady().then(() =\u0026gt; { createWindow() app.on(\u0026#39;activate\u0026#39;, function () { // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 // 打开的窗口,那么程序会重新创建一个窗口。 if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) // 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 // 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 app.on(\u0026#39;window-all-closed\u0026#39;, function () { if (process.platform !== \u0026#39;darwin\u0026#39;) app.quit() }) // 在这个文件中,你可以包含应用程序剩余的所有部分的代码, // 也可以拆分成几个文件,然后用 require 导入。 ####js\n创建一个新的文件preload.js。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 // preload.js // 所有Node.js API都可以在预加载过程中使用。 // 它拥有与Chrome扩展一样的沙盒。 window.addEventListener(\u0026#39;DOMContentLoaded\u0026#39;, () =\u0026gt; { const replaceText = (selector, text) =\u0026gt; { const element = document.getElementById(selector) if (element) element.innerText = text } for (const dependency of [\u0026#39;chrome\u0026#39;, \u0026#39;node\u0026#39;, \u0026#39;electron\u0026#39;]) { replaceText(`${dependency}-version`, process.versions[dependency]) } }) ####json\n为了确保能够运行相关electron的命令,需要修改package.json文件。\n首先需要去设置main属性,electron默认会去在开始时寻找项目根目录下的index.js文件,此处我们使用的是main.js,所以需要去定义下。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // package.json { \u0026#34;name\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;main.js\u0026#34;, // 新增 \u0026#34;scripts\u0026#34;: { \u0026#34;dev\u0026#34;: \u0026#34;vite\u0026#34;, \u0026#34;build\u0026#34;: \u0026#34;vite build\u0026#34;, \u0026#34;serve\u0026#34;: \u0026#34;vite preview\u0026#34; }, \u0026#34;dependencies\u0026#34;: { \u0026#34;vue\u0026#34;: \u0026#34;^3.2.16\u0026#34; }, \u0026#34;devDependencies\u0026#34;: { \u0026#34;@vitejs/plugin-vue\u0026#34;: \u0026#34;^1.9.3\u0026#34;, \u0026#34;electron\u0026#34;: \u0026#34;^15.1.2\u0026#34;, \u0026#34;vite\u0026#34;: \u0026#34;^2.6.4\u0026#34; } } 最后我们需要新增electron的运行命令。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // package.json { \u0026#34;name\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;main.js\u0026#34;, \u0026#34;scripts\u0026#34;: { \u0026#34;dev\u0026#34;: \u0026#34;vite\u0026#34;, \u0026#34;build\u0026#34;: \u0026#34;vite build\u0026#34;, \u0026#34;serve\u0026#34;: \u0026#34;vite preview\u0026#34;, \u0026#34;electron:serve\u0026#34;: \u0026#34;electron .\u0026#34; // 新增 }, \u0026#34;dependencies\u0026#34;: { \u0026#34;vue\u0026#34;: \u0026#34;^3.2.16\u0026#34; }, \u0026#34;devDependencies\u0026#34;: { \u0026#34;@vitejs/plugin-vue\u0026#34;: \u0026#34;^1.9.3\u0026#34;, \u0026#34;electron\u0026#34;: \u0026#34;^15.1.2\u0026#34;, \u0026#34;vite\u0026#34;: \u0026#34;^2.6.4\u0026#34; } } 运行 直接在终端输入如下命令:\n1 yarn electron:serve 接着我们就可以看到我们桌面应用就出来咯!\n最后 之前做项目一直用的Vue CLI Plugin Electron Builder,这次有个项目先用electron开发一下,推一波看看,后期看情况swift重新开发一个mac的桌面应用。也刚好尝尝鲜,一直没有机会试试vite。\nelectron这个东东确实很方便,就是打包出来的应用体积太大,真的是硬伤啊。这次目标人群首先是windows用户,所以上electron吧!\n参考文档 Electron官网快速入门 Vite官网 Build vue3 desktop apps in just 5 minutes ","date":"2021-10-18T09:58:57Z","permalink":"https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/","title":"Vite+Electron快速构建一个VUE3桌面应用"},{"content":"简介 在上一篇文章——前端文件花式直传OSS!后端:那我走?中聊了下文件上传的几种方案,这里我们再来聊一下文件下载的花式姿势。\n精简版 最常见的方式,莫过于后端存储文件在服务器上,然后通过后端接口传给前端,如下图所示。\n该方案对于小规模、成本较低的项目非常适用,开发也较为便捷。\n而对于有性能要求的项目,可以通过砸钱加机器、分片下载等方案提升项目性能。如果可以的话,请砸钱加机器吧!\n中庸版 相较于上一个方案,可以砸丢丢钱整个OSS,将文件存储在OSS上,毕竟OSS上行流量不收费,如下图所示。\n那么问题来了,OSS的下行流量不是收费的吗?!\nOK,偷偷告诉各位一个省钱小妙招/狗头,OSS内网的下行流量是不收费的!因此,可以通过后端请求OSS,获取到文件/字符串后,将其以文件流/base64数据的方式返回给前端。这样就避免了下行流量的费用。如下图所示。\n不过问题又来了,这样就还是占用了后端服务器的资源,依然会是性能的一个瓶颈。\n性能版 基于上一个方案,可以再升级。砸丢丢钱,拉上CDN这老哥,利用CDN流量代替OSS的下行流量,既能让前端直接请求OSS资源,不占用服务器资源,也降低了成本。如图所示。\n在优先性能的情况下,该方案是较优的。\n要说缺点的话,就是CDN配置吧,需要买域名和SSL证书等,不过一次购买,后续使用体验会非常棒。CDN除了可以代替OSS的下行流量外,其优点不要太多,比如说CDN可以文件缓存、可以调度至加速节点等。\n涉及到OSS的私有Bucket的话,只需要使用CDN的访问控制即可。其也只需要通过后端实现加密,生成文件访问URL给前端直接访问。\n或许你会存在疑问,看上去挺麻烦的啊!但是看看CDN的价格,你肯定会有不一样的想法的。\n","date":"2021-09-29T09:22:22Z","permalink":"https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/","title":"OSS花式解锁下载文件新姿势,你学废了吗?"},{"content":"简介 前端还在传文件给后端吗?你们的服务器扛得住吗?什么\u0026hellip;\u0026hellip;老板砸钱加机器?!告辞!/狗头\n前后端文件传输涉及数据较大,往往会成为很多项目的性能瓶颈。常见的传输方式也有不少,相对来说,OSS直传能够减轻很大压力。\n本文我们来列举下常见的和oss直传的几种传输方式,并列举其优劣。\n常见方式 表单上传 表单上传文件是最常见的方式,前后端开发小伙伴都很轻松,前端哐哐传,后端哐哐收就成了。其过程如下图所示。\n优势:\n简单方便,开发量小 前后端原生支持,无需额外第三方库支持 Base64上传 Base64方式上传文件,多常见于小文件,如小图片等,前后端都可直接使用String类型发送和接收。不过在前端,需要将文件转成base64数据,不仅会增加些性能消耗,还会增加传输数据的体积。而对于后端,如果并不是想直接存储base64数据,也还需要将其转成文件再存储,也会增加后端的性能消耗。\n该上传方式可适用于Resetful Api,也可适用于文件的加密、回调接口携带文件等等。其过程如下图所示。\n优势:\n适用于Resetful Api,可用于加密、回调等场景,较为灵活 劣势:\n前后端文件与base64数据转换需要消耗性能 只适用于小文件 OSS直传 此处的OSS直传方案都是使用的阿里云的OSS产品,以下将介绍三个方案,可适用于不同的场景。\nBrowser.js SDK上传 该方案可在前端直接通过browser.js上传文件到OSS,可分成三步:\n前端使用SDK直传OSS 前端上传完成后请求后端,通知上传完成 后端检测OSS上该文件是否存在(可选) 其流程如下图所示。\nBrowser.js的方式需要前端安装阿里云的库ali-oss,然后在前端调用。直传还需要OSS账户的Key和Secret,因此为了安全考虑,需要建立RAM账户,然后前端向后端先请求一个STS临时访问凭证来完成直传,其流程如下图所示。\nJavascript客户端签名直传 Javascript客户端签名直传,需要先从后端获取临时签名,其流程与上一步的browser.js方案大致相同,不同点在于:\n无需第三方库支持,直接表单上传 原生支持后端上传回调(下一步骤讲述) 其流程如下图所示。\n不过该方案做下来,我感觉最大的问题是权限配置有点麻烦\u0026hellip;\u0026hellip;/泪眼\n服务端签名直传并设置上传回调 该方案其实是上面方案——Javascript客户端签名直传的升级版本,其加上了后端的上传回调功能。不错呦~\n前端需要改动的很少,只需要在请求参数中加上callback参数即可,该参数为后端加密,在签名请求的响应中一起返回回来,内加密了后端回调接口。在前端直传完成后,后端回调接口将会接收到相关文件参数,包括文件路径、大小、类型等。最后OSS会将回调接口response转发给前端,响应直传OSS的请求。\n其流程如下图所示。\n对比 传统方式相比直传OSS,相对来说有三个缺点:\n上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。 当然,对于规模较小、成本较低的项目来说,常见的上传方式还是适合的,毕竟没有最好的,只有最适合的。\n参考文档 Web端上传介绍\nJavaScript客户端签名直传\n服务端签名直传并设置上传回调\n","date":"2021-09-28T09:53:18Z","permalink":"https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/","title":"前端文件花式直传OSS!后端:那我走?"},{"content":"前言 因为项目需求,需要去检测用户的农历生日。虽然后来找到了合适的库,但是首先先解释下农历的定义,也是去了解才知道,原来农历不是阴历。\n农历属于阴阳合历,其年份分为平年和闰年。平年为十二个月,闰年为十三个月。月份分为大月和小月,大月三十天,小月二十九天,其平均历月等于一个朔望月。\n环境 Go 1.16 github.com/nosixtools/solarlunar 0.0.0 库 1 github.com/nosixtools/solarlunar 该库支持1900~2049年。所以项目要跑到2049年后的童鞋就要注意\u0026hellip;\u0026hellip;\n当然,该库还支持阳历转农历、节假日计算等,有兴趣大家可以自行去了解下。\n使用 判断闰年 该库不支持闰年判断,所以需要自己去实现闰年的判断,其参数类型为Boolean。\n1 2 3 4 5 6 7 func IsALeapYear(year int) (result bool) { if year%4 == 0 \u0026amp;\u0026amp; year%100 != 0 || year%400 == 0 { result = true return } return } 转换 需要转换的阳历日期格式是固定的,是2006-01-02。此处以农历2021-07-17为例。\n1 2 3 4 func main() { lunarDate := \u0026#34;2021-07-17\u0026#34; fmt.Println(solarlunar.LunarToSolar(lunarDate, IsALeapYear(time.Now().Year()))) } 输出为:\n1 2021-08-24 全部代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; \u0026#34;github.com/nosixtools/solarlunar\u0026#34; ) func main() { lunarDate := \u0026#34;2021-07-17\u0026#34; fmt.Println(solarlunar.LunarToSolar(lunarDate, IsALeapYear(time.Now().Year()))) } func IsALeapYear(year int) (result bool) { if year%4 == 0 \u0026amp;\u0026amp; year%100 != 0 || year%400 == 0 { result = true return } return } ","date":"2021-08-31T09:08:57Z","permalink":"https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/","title":"Golang实现农历转换阳历"},{"content":"前言 IOS监听手势使用的方法为UISwipeGestureRecognizer。\n添加手势监听 1 2 3 4 let gesture = UISwipeGestureRecognizer() gesture.addTarget(self, action: #selector(yourSelector(gesture:))) gesture.direction = .left // .left左滑 .right右滑 .up上滑 .down下滑 self.addGestureRecognizer(gesture) 添加响应事件 1 2 3 @objc private func leftPushEvent(){ print(\u0026#34;响应...\u0026#34;) } 模板 把上面的整合起来,基本可以按照这个模板来写。\n1 2 3 4 5 6 7 8 @objc private func leftPushEvent(){ print(\u0026#34;响应...\u0026#34;) } let gesture = UISwipeGestureRecognizer() gesture.addTarget(self, action: #selector(leftPushEvent(gesture:))) gesture.direction = .left self.addGestureRecognizer(gesture) 参考文档 iOS手势识别\u0026ndash;上下左右滑动 ","date":"2021-08-30T14:29:02Z","permalink":"https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/","title":"IOS监听上下左右滑动手势"},{"content":"环境 Swift 5.4 Xcode 12.5.1 问题 在使用Swift UI和Realiy开发AR项目时,发现摄像头一直是居中的,无法全屏。\n解决 创建LaunchScreen.storyboard文件 在左侧文件列表中新建文件,名为LaunchScreen.storyboard。\n设置Launch Screen File 点击左侧文件列表中你的项目文件(最顶级文件),进入文件[your-project].xcodeproj文件。\n在General中,找到App Icons and Launch Images,在其模块中有Launch Screen File选项,点击选择为LaunchScreen.storyboard。\n总结下就是:[yourTarget] -\u0026gt; General -\u0026gt; App Icons and Launch Images。\n参考文档 How to make SwiftUI view fullscreen? ","date":"2021-08-30T14:11:58Z","permalink":"https://blog.hunterji.com/p/swiftui-reality%E5%BC%80%E5%8F%91ar%E9%A1%B9%E7%9B%AE%E8%A7%A3%E5%86%B3%E5%85%A8%E5%B1%8F%E9%97%AE%E9%A2%98/","title":"SwiftUI+Reality开发AR项目解决全屏问题"},{"content":"问题 开始是开发electron时遇到的问题,使用Interval计时器,在窗口最小化隐藏再打开,计时器在隐藏期间并没有工作。\n后来网上查询相关问题,发现更多是在浏览器tab页隐藏/切换情况下,计时器就会停止。\n解决 在后台选项卡上运行的计时器方法可能会耗尽资源。在后台选项卡中以非常短的时间间隔运行回调的应用程序可能会消耗大量内存,以至于当前活动选项卡的工作可能会受到影响。在最坏的情况下,浏览器可能会崩溃,或者设备的电池会很快耗尽。\n此限制是浏览器限制的。\n无法突破限制,但是可以使用折中的方式,当然我也觉得此方式相较于一直计时会更优,即监听visibilitychange事件。\nvisibilitychange事件可以监听tab页面的激活与失活事件,因此可以:\n在失活时,记录计时器计算的最后的值,清空计时器 在激活时,计算失活期间应有的值,继续使用计时器计算 添加事件代码如下: 1 2 3 4 5 6 7 8 9 10 document.addEventListener(\u0026#39;visibilitychange\u0026#39;, function() { if(document.hidden) { // tab页失活 } else { // tab页激活 } }); 参考文档 https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event\nhttps://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab\n","date":"2021-08-11T16:20:05Z","permalink":"https://blog.hunterji.com/p/interval%E8%AE%A1%E6%97%B6%E5%99%A8%E5%9C%A8tab%E9%A1%B5%E5%88%87%E6%8D%A2%E6%88%96%E8%80%85%E9%9A%90%E8%97%8F%E6%83%85%E5%86%B5%E4%B8%8B%E5%81%9C%E6%AD%A2%E8%BF%90%E8%A1%8C/","title":"Interval计时器在tab页切换或者隐藏情况下停止运行"},{"content":"报错 在Golang中json解析时报错:\n1 invalid character \u0026#39;\\\\b\u0026#39; after top-level value 代码如下:\n1 json.Unmarshal([]byte(result), \u0026amp;response) 分析与排错 首先将result打印出来,发现并无异常,其标点符号也没有问题。\n然后查看网上现有解决方案的帖子基本试了下,起码对于我来说并不适用,概括下方案:\n遍历然后过滤,最后重组; 遍历,使用SetEscapeHTML(false)禁用转义符; 编码; \u0026hellip; 最后对比代码中获取到的字符产长度和手动复制所见的字符串的长度,发现确实代码中字符长度不同,其长度是80,而手动复制的字符串的长度是72。\n解决 1 strings.ReplaceAll(result, \u0026#34;\\b\u0026#34;, \u0026#34;\u0026#34;) 就挺简单的\u0026hellip;\u0026hellip;\n","date":"2021-08-11T13:38:49Z","permalink":"https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/","title":"go中json解析报错invalid character '\\\\b' after top-level value"},{"content":"前言 项目开发中遇到该问题,网上的文章太乱,为了节省下次踩坑时间,特此记录。\n加解密 填充函数 该函数在加解密中都需要用到。\n1 2 3 4 5 func PKCS5Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(ciphertext, padText...) } 加密 1 2 3 4 5 6 7 8 9 10 11 func Ase256Encrypt(plaintext string, key string, iv string, blockSize int) string { bKey := []byte(key) bIV := []byte(iv) bPlaintext := PKCS5Padding([]byte(plaintext), blockSize) block, _ := aes.NewCipher(bKey) ciphertext := make([]byte, len(bPlaintext)) mode := cipher.NewCBCEncrypter(block, bIV) mode.CryptBlocks(ciphertext, bPlaintext) return base64.StdEncoding.EncodeToString(ciphertext) } 解密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func Aes256Decrypt(cryptData, key, iv string) ([]byte, error) { ciphertext, err := base64.StdEncoding.DecodeString(cryptData) if err != nil { return nil, err } block, err := aes.NewCipher([]byte(key)) if err != nil { return nil, err } if len(ciphertext)%aes.BlockSize != 0 { err = errors.New(\u0026#34;ciphertext is not a multiple of the block size\u0026#34;) return nil, err } mode := cipher.NewCBCDecrypter(block, []byte(iv)) mode.CryptBlocks(ciphertext, ciphertext) return ciphertext, err } 全部代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 // 填充 func PKCS5Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(ciphertext, padText...) } // 加密 func Ase256(plaintext string, key string, iv string, blockSize int) string { bKey := []byte(key) bIV := []byte(iv) bPlaintext := PKCS5Padding([]byte(plaintext), blockSize) block, _ := aes.NewCipher(bKey) ciphertext := make([]byte, len(bPlaintext)) mode := cipher.NewCBCEncrypter(block, bIV) mode.CryptBlocks(ciphertext, bPlaintext) return base64.StdEncoding.EncodeToString(ciphertext) } // 解密 func Aes256Decrypt(cryptData, key, iv string) ([]byte, error) { ciphertext, err := base64.StdEncoding.DecodeString(cryptData) if err != nil { return nil, err } block, err := aes.NewCipher([]byte(key)) if err != nil { return nil, err } if len(ciphertext)%aes.BlockSize != 0 { err = errors.New(\u0026#34;ciphertext is not a multiple of the block size\u0026#34;) return nil, err } mode := cipher.NewCBCDecrypter(block, []byte(iv)) mode.CryptBlocks(ciphertext, ciphertext) return ciphertext, err } 调用 加密 1 result := Ase256Encrypt(\u0026#34;\u0026lt;需要加密的数据\u0026gt;\u0026#34;, \u0026#34;\u0026lt;key\u0026gt;\u0026#34;, \u0026#34;\u0026lt;iv\u0026gt;\u0026#34;, aes.BlockSize) 解密 1 result, err := Aes256Decrypt(\u0026#34;\u0026lt;需要解密的数据\u0026gt;\u0026#34;, \u0026#34;\u0026lt;key\u0026gt;\u0026#34;, \u0026#34;\u0026lt;iv\u0026gt;\u0026#34;) ","date":"2021-08-11T13:13:12Z","permalink":"https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/","title":"Golang AES-256-CBC加密和解密"},{"content":"问题描述 使用electron开发的windows桌面应用程序,在调用目标文件夹底下的exe执行文件时,开发机子上没有问题,但是其他机子使用时一直调用失败,也抓取不到日志。\n1 2 3 4 5 spawn(path.join(remote.app.getAppPath(), \u0026#34;../target.exe\u0026#34;), [], { shell: true, detached: false, windowsHide: true }); 原因 路径存在空格。\n也是经过各种原因排查,然后一次偶然的成功才注意到了路径问题,排查之后发现确实是这问题\u0026hellip;\u0026hellip;\n解决 spawn按照如上我的代码一定条件下可以运行,其有一个参数cwd,用来表明运行目录。spawn第一个参数必须是命令的名字,不能是路径。\n所以如上代码改成这样:\n1 2 3 4 5 6 spawn(\u0026#34;target.exe\u0026#34;, [], { // 此处直接写目标exe文件 cwd: path.join(remote.app.getAppPath(), \u0026#34;../\u0026#34;), // 注意这里,使用了cwd参数来写运行目录 shell: true, detached: false, windowsHide: true }); 参考文档 node child_process.spawn not working with spaces in path on windows ","date":"2021-07-29T13:49:13Z","permalink":"https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/","title":"node spawn在windows下不生效问题记录"},{"content":"问题 使用Alert时,将其用在list的循环视图元素中,弹出Alert时,一定时长不选择就会在点击后弹出第二次。\n这里提一下就是之前在网上看到一个帖子说他将Alert放在NavigationView上也会出现该问题。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 VStask { ForEach(items, id: \\.self) { item in ElementView(item: item) // 循环中的元素 .alert(isPresented: $showAlert) { Alert( title: Text(\u0026#34;删除确认\u0026#34;), message: Text(\u0026#34;请问您确认删除该数据吗?\u0026#34;), primaryButton: .default( Text(\u0026#34;取消\u0026#34;), action: { showAlert = false } ), secondaryButton: .destructive( Text(\u0026#34;删除\u0026#34;), action: { deleteItems(offsets: [index]) }) ) } } } 解决 将Alert放到循环之前的元素上,比如VStack、List。\n参考 参考帖子 ","date":"2021-07-12T16:40:57Z","permalink":"https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AEalert%E5%BC%B9%E5%87%BA%E4%B8%A4%E6%AC%A1%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/","title":"SwiftUI MacOS项目alert弹出两次问题解决"},{"content":"实现 1 2 3 4 5 @Environment(\\.colorScheme) var colorScheme var isLight: Bool { colorScheme == .light } 调用 1 2 Text(\u0026#34;Hello, World !\u0026#34;) .foregroundColor(isLight ? Color.red : Color.green) 完整例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import SwiftUI struct CheckIsLight: View { @Environment(\\.colorScheme) var colorScheme var isLight: Bool { colorScheme == .light } var body: some View { Text(\u0026#34;Hello, World !\u0026#34;) .foregroundColor(isLight ? Color.red : Color.green) // 此处使用isLght实现根据暗黑模式切换字体颜色 } } struct CheckIsLight_Previews: PreviewProvider { static var previews: some View { CheckIsLight() } } ","date":"2021-06-08T09:20:38Z","permalink":"https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E6%9A%97%E9%BB%91%E6%A8%A1%E5%BC%8F/","title":"SwiftUI项目判断是否为暗黑模式"},{"content":"设置权限 打开文件info.plist,在空白处右击,选择Add Row,输入选择Privacy - Face ID Usage Description,然后在value中写入我们需要验证您的身份以保护数据。\n代码层面接入 打开ContentView.swift文件,开始如下操作。\n引入相关库 1 import LocalAuthentication 创建lock变量 1 @State private var isUnlocked = false isUnlocked为是否解锁,true表示验证完成,已解锁,false表示验证失败,未解锁。\n创建函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func authenticate() { let context = LAContext() var error: NSError? // 检查生物特征认证是否可用 if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: \u0026amp;error) { // 可用,所以继续使用它 let reason = \u0026#34;我们需要验证您的身份以保护数据\u0026#34; context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in // 身份验证现已完成 DispatchQueue.main.async { if success { // 认证成功,解锁 self.isUnlocked = true } else { // 发生的异常 } } } } else { // 没有生物识别 } } 根据isUnlocked切换View 1 2 3 4 5 6 7 8 VStack { if self.isUnlocked { Text(\u0026#34;Unlocked\u0026#34;) } else { Text(\u0026#34;Locked\u0026#34;) } } .onAppear(perform: authenticate) // 该方法调用生物识别验证函数 审核问题 最近提交了ios和macos两个产品到app store,我设置的强制使用生物识别才能进入应用。但是出现的问题是macos的审核过了,而ios的审核没有过,其反馈的问题即为开始的生物识别没有过,审核人员使用的模拟器,根本不存在生物识别。\n所以跟可能会踩这个坑的小伙伴儿提个醒,目前我还没有好的解决方案,正在等待新的审核中\u0026hellip;\u0026hellip;\n","date":"2021-06-07T17:00:00Z","permalink":"https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8%E7%94%9F%E7%89%A9%E8%AF%86%E5%88%ABtouch-id--face-id/","title":"SwiftUI项目调用生物识别(Touch ID -- Face ID)"},{"content":"代码实现 获取屏幕对象 1 var window = NSScreen.main?.visibleFrame 设置大小 1 2 3 4 HStack { } .frame(width: window!.width / 2.0, height: window!.height / 1.5) 汇总 1 2 3 4 5 6 7 8 9 10 11 struct Home: View { var window = NSScreen.main?.visibleFrame var body: some View { HStack { Text(\u0026#34;Hello, World!\u0026#34;) } .frame(width: window!.width / 2.0, height: window!.height / 1.5) } } ","date":"2021-06-07T16:55:50Z","permalink":"https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AE%E6%A0%B9%E6%8D%AE%E5%B1%8F%E5%B9%95%E5%A4%A7%E5%B0%8F%E8%B0%83%E6%95%B4%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F/","title":"SwiftUI MacOS项目根据屏幕大小调整窗口大小"},{"content":"实现 创建变量 1 @State var search: String = \u0026#34;\u0026#34; 过滤 此处过滤条件为判断元素是否包含搜索的文本。\n1 \u0026lt;Your-Array\u0026gt;.filter({\u0026#34;\\($0)\u0026#34;.contains(search.lowercased()) || search.isEmpty}) 汇总 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct DataList: View { @State var search: String = \u0026#34;\u0026#34; @Binding var dataList: [Item] var dataSearchFilterList: [Item] { dataList.filter({\u0026#34;\\($0)\u0026#34;.contains(search.lowercased()) || search.isEmpty}) } var body: some View { if dataSearchFilterList.isEmpty { Text(\u0026#34;搜索不到...\u0026#34;) } else { ... // 展示搜索结果 } } } ","date":"2021-06-07T16:55:39Z","permalink":"https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%8A%9F%E8%83%BD/","title":"SwiftUI项目实现搜索功能"},{"content":"前言 swiftui中的点击可以有两种情况:\nButton Gestures 根据不同情况可以去不同地使用。\n单纯的按钮 此处单纯的按钮即为有按钮样式和点击事件。\n1 2 3 4 5 Button(action: { ... // 点击事件触发的代码 }, label: { Image(systemName: \u0026#34;plus\u0026#34;) }) 无样式的按钮 即为没有按钮样式的按钮,方便直接展示Image。\n1 2 3 4 5 6 Button(action: { ... // 点击事件触发的代码 }, label: { Image(systemName: \u0026#34;plus\u0026#34;) }) .buttonStyle(BorderlessButtonStyle()) TapGesture事件 1 2 3 4 Image(systemName: \u0026#34;plus\u0026#34;) .onTapGesture { ... // 点击事件触发的代码 } ","date":"2021-06-07T16:43:17Z","permalink":"https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AEimage%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6/","title":"SwiftUI项目Image点击事件"},{"content":"前言 这是个比较坑的问题,我一开始开发的是macos项目,到网上搜的方案基本都是使用UIPasteboard方法,但是偏偏用不了。\n后来开发ios项目,用macos的就不行,发现UIPasteboard的可行,所以这里需要清楚的是,ios和macos的复制方法是不同的\u0026hellip;\u0026hellip;\nMacOS 实现 1 2 3 4 5 func copyToClipBoard(textToCopy: String) { let pasteBoard = NSPasteboard.general pasteBoard.clearContents() pasteBoard.setString(textToCopy, forType: .string) } 调用 1 copyToClipBoard(textToCopy: \u0026#34;Hello,World!\u0026#34;) IOS 实现 1 UIPasteboard.general.setValue(\u0026lt;Your-String\u0026gt;, forPasteboardType: kUTTypePlainText as String) 调用 1 UIPasteboard.general.setValue(\u0026#34;Hello,World!\u0026#34;, forPasteboardType: kUTTypePlainText as String) ","date":"2021-06-07T16:29:41Z","permalink":"https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%A4%8D%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/","title":"SwiftUI项目复制字符串到剪切板"},{"content":"官方方法 DateComponents A date or time specified in terms of units (such as year, month, day, hour, and minute) to be evaluated in a calendar system and time zone.\n以要在日历系统和时区中计算的单位(例如年、月、日、小时和分钟)指定的日期或时间。\n实现 计算两个字符串形式的日期的天数差 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func dateDiff() -\u0026gt; Int { // 计算两个日期差,返回相差天数 let formatter = DateFormatter() let calendar = Calendar.current formatter.dateFormat = \u0026#34;yyyy-MM-dd\u0026#34; let today = Date() // 开始日期 let startDate = formatter.date(from: \u0026#34;2021-06-08\u0026#34;) // 结束日期 let endDate = formatter.date(from: \u0026#34;2021-06-09\u0026#34;) let diff:DateComponents = calendar.dateComponents([.day], from: startDate!, to: endDate!) return diff.day! } 计算当天跟某一天的天数差 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func checkDiff() -\u0026gt; Int { // 计算两个日期差,返回相差天数 let formatter = DateFormatter() let calendar = Calendar.current formatter.dateFormat = \u0026#34;yyyy-MM-dd\u0026#34; // 当天 let today = Date() let startDate = formatter.date(from: formatter.string(from: today)) // 固定日期 let endDate = formatter.date(from: \u0026#34;2021-06-09\u0026#34;) let diff:DateComponents = calendar.dateComponents([.day], from: startDate!, to: endDate!) return diff.day! } ","date":"2021-06-07T15:12:29Z","permalink":"https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/","title":"Swift计算两个日期的天数差"},{"content":"前言 这篇文章是我写的第一篇Swift UI相关的笔记吧。\n这真的难搞哦,毕竟新出来的。国内文档真的是少之又少,国外的文档也真不多,基本搜出来的都是UIKit的。官方文档,也是一言难尽,基本就处于,我要实现一个功能,然后去搜一下各种帖子,筛选掉无用的帖子,找到有用的点,当然也是时常根本找不到有用的帖子,然后就要去油管上看各种教程,然后发现:哦!原来还有这个方法!接着去官方文档搜一下,看一下属性,自己调用调用\u0026hellip;\u0026hellip;\n光是这个core data我就折腾了近一周,最后发现,原来还是官方好呀\u0026hellip;..\n吐槽一下自学swift ui,现在进入正题了。\ncore data呢,是苹果官方的本地数据库,但是其存储的文件其实是sqlite文件。其可以通过icloud实现备份和同步,当然icloud我会额外写一篇文档来详细讲述的(又是一把辛酸泪\u0026hellip;)。\n环境 如下是我当前的环境:\n系统:macOS Big Sur 11.4 Xcode:Version 12.5 (12E262) Swift:5.4 操作步骤 创建项目 在创建项目的时候,可以直接选择Use Core Data选项,xcode会直接在ContentView.swift中生成一个相关demo。\n查看相关文件 创建之后,查看文件目录,相对于不选择Use Core Data,会多出如下几个文件:\n.xcdatamodeId Persistence.swift 创建core data表 点击\u0026lt;Your-Project-Name\u0026gt;.xcdatamodeId文件,进入页面。\n可以看到页面内有默认的CONFIGURATIONS为Default,默认的ENITITIES为Item,可以理解为分别对应sql中的库和表。\n然后点击Item,可以看到其字段,有默认的timestamp字段。\n若要新增ENTITIES(表),点击底部的Add Entity按钮即可。\n若要新增Attributes(字段),点击右侧Attributes中的+即可,注意字段要选择类型。\n比如,此处以默认的Item为例,新增username和age两个字段。\n代码层面操作core data 1)查看 1 2 3 4 5 6 7 8 @Environment(\\.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \\Item.timestamp, ascending: true)], animation: .default) private var items: FetchedResults\u0026lt;Item\u0026gt; // body中便利items即可 2)新增 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private func addItem() { withAnimation { let newItem = Item(context: viewContext) newItem.timestamp = Date() do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError(\u0026#34;Unresolved error \\(nsError), \\(nsError.userInfo)\u0026#34;) } } } 如上是xcode自动生成的新增函数,若是要自定义添加字段可以这样改动下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 此处按照如上添加Attributes修改,具体修改按照项目具体情况 private func addItem(username: String, age: Int16) { withAnimation { let newItem = Item(context: viewContext) newItem.username = username newItem.age = age newItem.timestamp = Date() do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError(\u0026#34;Unresolved error \\(nsError), \\(nsError.userInfo)\u0026#34;) } } } 3)删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 private func deleteItems(offsets: IndexSet) { withAnimation { offsets.map { items[$0] }.forEach(viewContext.delete) do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError(\u0026#34;Unresolved error \\(nsError), \\(nsError.userInfo)\u0026#34;) } } } 4)汇总 在默认生成代码中,有一个toolbar,其在macos中可以生效,但是在ios中只有EditionButton()可以使用,为了方便演示,此处新增一个Button来添加数据。\n其次有点要声明下,在xcode中写代码时,右侧的canvas会实时渲染,列表中出现的数据并不是core data中的数据,而是默认生成的Persistence.swift中生成的演示数据,只能看看,不能当真。只有在模拟器/实体机编译运行时才能操作core data。\n如下为修改过后的ContentView.swift文件:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 // // ContentView.swift // HelloKuari // // Created by Kuari on 2021/6/5. // import SwiftUI import CoreData struct ContentView: View { @Environment(\\.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \\Item.timestamp, ascending: true)], animation: .default) private var items: FetchedResults\u0026lt;Item\u0026gt; var body: some View { VStack { List { ForEach(items) { item in Text(\u0026#34;Tom: \\(item.username!) age: \\(item.age) time : \\(item.timestamp!, formatter: itemFormatter)\u0026#34;) } .onDelete(perform: deleteItems) } // 新增一个按钮来添加数据 Button(action: { addItem(username: \u0026#34;tom\u0026#34;, age: 12) }, label: { Text(\u0026#34;Add Item\u0026#34;) }) } } private func addItem(username: String, age: Int16) { withAnimation { let newItem = Item(context: viewContext) newItem.username = username newItem.age = age newItem.timestamp = Date() do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError(\u0026#34;Unresolved error \\(nsError), \\(nsError.userInfo)\u0026#34;) } } } private func deleteItems(offsets: IndexSet) { withAnimation { offsets.map { items[$0] }.forEach(viewContext.delete) do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError(\u0026#34;Unresolved error \\(nsError), \\(nsError.userInfo)\u0026#34;) } } } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium return formatter }() struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView().environment(\\.managedObjectContext, PersistenceController.preview.container.viewContext) } } 然后点击左侧顶部的运行按钮,编译运行。\n一开始是空白一片,点击Add Item按钮之后,便会开始添加数据。\n对于记录右滑即可删除,其为List的onDelete方法。\n结语 该文章是面向新手的,也是记录下我踩过的坑,因为目前文档匮乏,身边也没swift的开发小伙伴儿,只能靠自己摸索,若有大佬有更好的方法,真的还请不吝赐教。\n后面持续记录踩坑中\u0026hellip;\u0026hellip;\n","date":"2021-06-07T11:24:22Z","permalink":"https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/","title":"Swift UI项目调用core data"},{"content":"简介 要实现h5检测手机摇一摇动作可以直接调用h5原生api。但是在我的实践中发现在ios中限制条件比较多,体验还是有些区别的。\n如何监听 调用Window: devicemotion event即可实现监听。devicemotion事件以固定的时间间隔触发,并指示设备当时在接收的加速物理力量。 它还提供有关旋转速率的信息(如果有)。\n1 2 3 4 5 6 7 8 9 10 function handleMotionEvent(event) { var x = event.accelerationIncludingGravity.x; var y = event.accelerationIncludingGravity.y; var z = event.accelerationIncludingGravity.z; // Do something awesome. } window.addEventListener(\u0026#34;devicemotion\u0026#34;, handleMotionEvent, true); 安卓机 安卓机上直接按照如上即可实现。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;测试摇一摇\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;phone\u0026#34;\u0026gt; \u0026lt;div id=\u0026#34;show\u0026#34;\u0026gt;摇一摇\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;script\u0026gt; function handleMotionEvent(event) { document.getElementById(\u0026#39;show\u0026#39;).innerHTML = \u0026#39;摇动中\u0026#39; } if (window.DeviceMotionEvent) { window.addEventListener(\u0026#34;devicemotion\u0026#34;, handleMotionEvent, false); } else { alert(\u0026#34;该浏览器不支持摇一摇功能\u0026#34;); } \u0026lt;/script\u0026gt; \u0026lt;/html\u0026gt; iPhone 限制 在ios上限制有两条:\nh5必须是https协议的 必须用户点击授权才可以调用devicemotion 授权 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function getPermission() { if ( typeof window.DeviceMotionEvent !== \u0026#39;undefined\u0026#39; \u0026amp;\u0026amp; typeof window.DeviceMotionEvent.requestPermission === \u0026#39;function\u0026#39; ) { window.DeviceMotionEvent.requestPermission() .then(function(state) { if (\u0026#39;granted\u0026#39; === state) { //用户同意授权 } else { //用户拒绝授权 alert(\u0026#39;摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!\u0026#39;) } }) .catch(function(err) { alert(\u0026#39;error: \u0026#39; + err) }) } } 直接调用该函数请求授权会导致报错:\n1 error: NotAllowedError: Requesting device orientation or motion access requires a user gesture to prompt 需要用户主动去请求授权,因此此处需要将调用放到比如一个按钮上,让用户去点击请求授权。\n1 \u0026lt;button onclick=\u0026#34;getPermission()\u0026#34;\u0026gt;请求授权\u0026lt;/button\u0026gt; 全部代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;测试摇一摇\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;phone\u0026#34;\u0026gt; \u0026lt;button onclick=\u0026#34;getPermission()\u0026#34;\u0026gt;请求授权\u0026lt;/button\u0026gt; \u0026lt;div id=\u0026#34;show\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;script\u0026gt; function handleMotionEvent(event) { document.getElementById(\u0026#39;show\u0026#39;).innerHTML = \u0026#39;摇动中\u0026#39; } function startListen() { if (window.DeviceMotionEvent) { window.addEventListener(\u0026#34;devicemotion\u0026#34;, handleMotionEvent, false); } else { alert(\u0026#34;该浏览器不支持摇一摇功能\u0026#34;); } } function getPermission() { if ( typeof window.DeviceMotionEvent !== \u0026#39;undefined\u0026#39; \u0026amp;\u0026amp; typeof window.DeviceMotionEvent.requestPermission === \u0026#39;function\u0026#39; ) { window.DeviceMotionEvent.requestPermission() .then(function(state) { if (\u0026#39;granted\u0026#39; === state) { //用户同意授权 startListen() } else { //用户拒绝授权 alert(\u0026#39;摇一摇需要授权设备运动权限,请重启应用后,再次进行授权!\u0026#39;) } }) .catch(function(err) { alert(\u0026#39;error: \u0026#39; + err) }) } } \u0026lt;/script\u0026gt; \u0026lt;/html\u0026gt; ","date":"2021-04-28T13:07:50Z","permalink":"https://blog.hunterji.com/p/h5%E6%A3%80%E6%B5%8B%E6%89%8B%E6%9C%BA%E6%91%87%E4%B8%80%E6%91%87/","title":"H5检测手机摇一摇"},{"content":"简介 具体日志采集方案在Grafana+Loki+Promtail日志收集方案文章中已经介绍过,此处不再重复介绍。不太了解的小伙伴儿赶紧去复习!\n此处主要是记录下Docker Driver Client方式的部署。\ndocker plugin 安装 安装loki插件。\n1 docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions 验证 1 2 3 $ docker plugin ls ID NAME DESCRIPTION ENABLED ac720b8fcfdb loki Loki Logging Driver true 开启/禁用 1 2 docker plugin enable loki docker plugin disable loki --force 卸载 1 docker plugin rm loki 部署 docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 version: \u0026#34;3\u0026#34; networks: loki: services: loki: image: grafana/loki:2.0.0 ports: - \u0026#34;3100:3100\u0026#34; command: -config.file=/etc/loki/local-config.yaml networks: - loki grafana: image: grafana/grafana:latest ports: - \u0026#34;3000:3000\u0026#34; networks: - loki 运行 1 docker-compose up -d 容器运行形式 容器运行时需要修改log-driver:\n1 2 3 4 --log-driver=loki --log-opt loki-url=\u0026#34;http://\u0026lt;loki-url\u0026gt;/loki/api/v1/push\u0026#34; --log-opt loki-retries=5 --log-opt loki-batch-size=400 举个例子:\n1 2 3 4 5 6 docker run --log-driver=loki \\ --log-opt loki-url=\u0026#34;http://192.168.10.10:3100/loki/api/v1/push\u0026#34; \\ --log-opt loki-retries=5 \\ --log-opt loki-batch-size=400 \\ -p 3000:3000 \\ nginx 由此查看日志,其labels只有container_name。\n参考文档 官方文档 ","date":"2021-04-14T15:13:18Z","permalink":"https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/","title":"Grafana+Loki+Docker Driver Client日志收集方案"},{"content":"前言 简介 Grafana项目由TorkelÖdegaard于2014年发起,最近几年成为GitHub上最受欢迎的开源项目之一。 它使您可以查询,可视化指标并记录警报,无论它们存储在何处。\nLoki是受Prometheus启发的水平可扩展,高度可用的多租户日志聚合系统。 它被设计为非常经济高效且易于操作。 它不索引日志的内容,而是为每个日志流设置一组标签。\nPromtail是将本地日志内容发送到私有Loki实例或Grafana Cloud的代理。 通常将其部署到需要监视应用程序的每台机器上。\n方案对比 为什么选择Grafana + Loki + Promtail的日志采集方案呢?\n我尝试过如下几种方案:\nElasticsearch+Kibana+Filebeat: 运维成本低,侵入性低,但是对于高并发情况下效果不太好,消耗资源也稍高,需要考虑日志存储成本 Elasticsearch+Kibana+Logstash+Kafka+Filebeat: 可以有效处理高并发情况,且在elk节点挂掉情况下不会丢失日志。但是,运维成本高,需要考虑日志存储成本,整套消耗资源比较高! Grafana+Loki+Docker Driver Client:使用Docker Driver的方式来直接获取容器的日志,配置较简单,但是需要物理机上安装docker plugin,和运行容器时设置log-driver,侵入性较高 相比之下,当前选择的方案,对于我们当前业务场景下是较为合适的,轻量且侵入性低,由于是检测日志文件,无需担心存储成本。\n通用日志收集 首先介绍下通用日志收集版本的部署。只需要使用默认配置即可收集普通日志,可在http://\u0026lt;your-ip\u0026gt;:3000上查看日志详情。\ndocker-compose 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 version: \u0026#34;3\u0026#34; networks: loki: services: loki: image: grafana/loki:2.0.0 ports: - \u0026#34;3100:3100\u0026#34; command: -config.file=/etc/loki/local-config.yaml networks: - loki promtail: image: grafana/promtail:2.0.0 volumes: - /var/log:/var/log command: -config.file=/etc/promtail/config.yml networks: - loki grafana: image: grafana/grafana:latest ports: - \u0026#34;3000:3000\u0026#34; networks: - loki 添加数据源 部署成功之后,打开http://\u0026lt;your-ip\u0026gt;:3000访问Grafana,在左侧菜单栏选择Configuration,默认进去Data Sources页面。\n点击Add data sources按钮,选择Loki。\n填入URL即可,此处为http://loki:3100,具体要看实际部署。\n然后点击Sace \u0026amp; Test添加。\n查看与筛选日志 在左侧菜单栏选择Explore进入页面,点击左上角的Log brwser按钮,可以查看该数据源的labels,如此处为日志文件。\n在页面顶部的输入框中输入官方的LogQL可以筛选日志。此处日志就不展示了,大家知道有就行了。\nDocker容器日志收集 此处详细介绍下关于docker容器日志收集。\npromtail配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 ##config.yaml server: http_listen_address: 0.0.0.0 http_listen_port: 9080 positions: filename: /tmp/positions.yaml clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: containers static_configs: - targets: - localhost labels: job: containerlogs __path__: /var/lib/docker/containers/*/*log pipeline_stages: - json: expressions: output: log stream: stream attrs: - json: expressions: tag: source: attrs - regex: expression: (?P\u0026lt;container_name\u0026gt;(?:[^|]*[^|])) source: \u0026#34;tag\u0026#34; - timestamp: format: RFC3339Nano source: time - labels: # tag: stream: container_name: - output: source: output docker-compose 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 version: \u0026#34;3\u0026#34; networks: loki: services: loki: image: grafana/loki:2.0.0 ports: - \u0026#34;3100:3100\u0026#34; command: -config.file=/etc/loki/local-config.yaml networks: - loki promtail: image: grafana/promtail:2.0.0 volumes: - /var/lib/docker/containers:/var/lib/docker/containers - ./promtail-config.yaml:/mnt/config/promtail-config.yaml command: -config.file=/mnt/config/promtail-config.yaml networks: - loki grafana: image: grafana/grafana:latest ports: - \u0026#34;3000:3000\u0026#34; networks: - loki 容器日志展现形式 至此其实就已经可以在Grafana上看到当前容器的日志了,操作如上通用日志采集,但是其展现形式只是filename,也就是类似于107728869f40afa5510879a0e372c77bb513d6154591193d375bfcd421357ed4.log的以container_id展现的日志文件,难以辨认具体是哪个容器。(要是有功夫去登录服务器看下容器id,不如直接看下日志了\u0026hellip;)\n所以,为了能够使用容器名去查看日志,此处需要在容器启动时设置参数:\n1 --log-driver json-file --log-opt tag=\u0026#34;{{.Name}}\u0026#34; 举个例子:\n1 docker run --name hello -p 8080:80 --log-driver json-file --log-opt tag=\u0026#34;{{.Name}}\u0026#34; -d nginx 对于使用container name,还有另一种方案,就是每次生成container_name和container_id的映射表,个人认为比较麻烦,有兴趣的小伙伴儿可以尝试下。\n参考文档 Loki官方文档 ruanbekker/promtail_docker_logs Display docker logs with human/logical name ","date":"2021-04-14T14:41:47Z","permalink":"https://blog.hunterji.com/p/grafana-loki-promtail%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/","title":"Grafana+Loki+Promtail日志收集方案"},{"content":"问题 在使用axios的拦截器时候,需要在request中调用一个promise函数,因此需要等待其执行完成才能去进行下一步。\n1 2 3 4 5 6 7 8 9 function getToken() { return new Promise(...) } // Request interceptors service.interceptors.request.use(config =\u0026gt; { getToken() ... }) 解决 1 2 3 4 5 6 7 8 9 function getToken() { return new Promise(...) } // Request interceptors service.interceptors.request.use(async config =\u0026gt; { awit getToken() ... }) ","date":"2021-04-09T11:06:25Z","permalink":"https://blog.hunterji.com/p/promise-inside-request-interceptor/","title":"Promise inside request interceptor"},{"content":"简介 Rapidly build modern websites without ever leaving your HTML.\nTailwind CSS可以快速建立现代网站,而无需离开HTML。其特性是原子化,很像的BootStrap的css。\n通俗点解释就是,其封装了很多独立的css样式,只需要在html中添加class即可调用,而不需要去从头写css样式。\n安装 下载包 1 npm install tailwindcss@latest postcss@latest autoprefixer@latest 可能会遇到如下报错:\n1 Error: PostCSS plugin tailwindcss requires PostCSS 8. 那就需要降低PostCSS的版本。如下,先卸载,再去安装。\n1 2 npm uninstall tailwindcss postcss autoprefixer npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9 添加Tailwind作为PostCSS插件 添加tailwindcss和autoprefixer到PostCSS配置。大部分情况下作为postcss.config.js文件放在项目的顶级路径下。其也能作为.postcssrc文件,或者使用postcss键放在package.json文件中。\n1 2 3 4 5 6 7 // postcss.config.js module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, } } 创建配置文件 如果想自定义安装,当使用npm安装tailwindcss时候需要使用tailwind命令行去生成一个配置文件。\n1 npx tailwindcss init 这将会创建一个最小化的tailwind.config.js文件,其位于项目的顶级路径下。\n1 2 3 4 5 6 7 8 9 10 // tailwind.config.js module.exports = { purge: [], darkMode: false, // or \u0026#39;media\u0026#39; or \u0026#39;class\u0026#39; theme: { extend: {}, }, variants: {}, plugins: [], } 在CSS中包含Tailwind 创建styles.css文件。\n1 2 3 4 /* ./your-css-folder/styles.css */ @tailwind base; @tailwind components; @tailwind utilities; 引入该文件。\n1 import \u0026#34;./styles.css\u0026#34; 构建CSS 为生产而构建时,确保配置清除选项以删除任何最小文件大小的未使用类。\n1 2 3 4 5 6 7 8 9 10 11 12 // tailwind.config.js module.exports = { purge: [\u0026#34;./index.html\u0026#34;, \u0026#34;./src/**/*.{vue,js,ts,jsx,tsx}\u0026#34;], // 修改此行 darkMode: false, // or \u0026#39;media\u0026#39; or \u0026#39;class\u0026#39; theme: { extend: {} }, variants: { extend: {} }, plugins: [] }; 简要说明 由于其样式属性巨多,此处只举几例作简要说明,讲解基础用法。在开始不熟悉的情况下,要开着其手册查询。\nWidth Class 解释 w-0 width: 0px; w-1 width: 0.25rem; w-1/2 width: 50%; w-full width: 100%; \u0026hellip; \u0026hellip; 1 2 3 4 \u0026lt;!--示例--\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026#34;w-1/2\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Padding Class 解释 p-0 padding: 0px; p-5 padding: 1.25rem; pl-1 padding-left: 0.25rem; \u0026hellip; \u0026hellip; 1 2 \u0026lt;!--示例--\u0026gt; \u0026lt;div class=\u0026#34;p-5\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; Position Class 解释 static position: static; fixed position: fixed; absolute position: absolute; \u0026hellip; \u0026hellip; 1 2 3 4 5 6 7 \u0026lt;!--示例--\u0026gt; \u0026lt;div class=\u0026#34;static\u0026#34;\u0026gt; \u0026lt;p\u0026gt;Static parent\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026#34;absolute bottom-0 left-0 ...\u0026#34;\u0026gt; \u0026lt;p\u0026gt;Absolute child\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Flex垂直居中 1 2 3 4 5 \u0026lt;div class=\u0026#34;flex flex-row justify-center items-center\u0026#34;\u0026gt; \u0026lt;div\u0026gt;1\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;2\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; 简单案例 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;div class=\u0026#34;flex flex-col justify-center items-center p-20\u0026#34;\u0026gt; \u0026lt;div v-for=\u0026#34;item in 10\u0026#34; :key=\u0026#34;item\u0026#34; class=\u0026#34;flex flex-row justify-between items-center w-1/5 bg-gray-100 m-5 p-10 cursor-pointer shadow rounded hover:shadow-lg transition duration-300 ease-in-out\u0026#34;\u0026gt; \u0026lt;img src=\u0026#34;@/assets/message.png\u0026#34; alt=\u0026#34;logo\u0026#34; height=\u0026#34;50px\u0026#34; width=\u0026#34;50px\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;flex flex-col ml-5\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;text-lg\u0026#34;\u0026gt;今天晚上加{{ item }}个鸡腿\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;text-sm text-gray-500\u0026#34;\u0026gt;2020.2.{{ item }}\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ","date":"2021-02-05T11:16:28Z","permalink":"https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/","title":"tailwindcss基础"},{"content":"前言 为什么要用sqlx而不是gorm呢?是因为orm学习成本比较高,当使用python时候需要使用sqlalchemy,遇到go就要换成gorm,换成别的语言就又有其他orm。而直接使用原生sql可以减少学习成本,适用于所有开发语言。其次,gorm本身支持软删除,但是其对软删除的支持上存在缺陷,在单条查询可以过滤软删除数据,但是在多条查询时无法有效过滤,就造成了有时候要手动过滤又有时候不要手动过滤,使用体验非常差。\n因此此处考虑去使用sqlx来直接调用原生sql。\n安装 1 go get github.com/jmoiron/sqlx 连接数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package database import ( \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; _ \u0026#34;github.com/go-sql-driver/mysql\u0026#34; \u0026#34;github.com/jmoiron/sqlx\u0026#34; ) func DBConnect() *sqlx.DB { env := os.Getenv(\u0026#34;NODE_ENV\u0026#34;) var host, port, user, password, dbname string // 使用环境变量来切换生产和开发环境 if env == \u0026#34;production\u0026#34; { host = os.Getenv(\u0026#34;dbHost\u0026#34;) port = os.Getenv(\u0026#34;dbPort\u0026#34;) user = os.Getenv(\u0026#34;dbUser\u0026#34;) password = os.Getenv(\u0026#34;dbPassword\u0026#34;) dbname = os.Getenv(\u0026#34;dbname\u0026#34;) } else { host = \u0026#34;\u0026lt;your-host\u0026gt;\u0026#34; port = \u0026#34;\u0026lt;your-port\u0026gt;\u0026#34; user = \u0026#34;\u0026lt;your-user\u0026gt;\u0026#34; password = \u0026#34;\u0026lt;your-password\u0026gt;\u0026#34; dbname = \u0026#34;\u0026lt;your-db-name\u0026gt;\u0026#34; } dbConfig := fmt.Sprintf(\u0026#34;%s:%s@tcp(%s:%s)/%s?parseTime=true\u0026#34;, user, password, host, port, dbname) db, err := sqlx.Connect(\u0026#34;mysql\u0026#34;, dbConfig) if err != nil { fmt.Println(err) panic(\u0026#34;failed to connect database\u0026#34;) } return db } 创建表和调用 创建表 1 2 3 4 5 6 7 8 9 10 11 create table if not exists test_gin.todo ( todo_id int auto_increment primary key, title varchar(20) not null comment \u0026#39;todo标题\u0026#39;, content varchar(200) null comment \u0026#39;内容\u0026#39;, user_id int not null comment \u0026#39;用户id\u0026#39;, created_at timestamp default CURRENT_TIMESTAMP null comment \u0026#39;创建时间戳\u0026#39;, updated_at timestamp default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment \u0026#39;更新时间戳\u0026#39;, is_deleted tinyint(1) default 0 not null comment \u0026#39;是否被删除,0:未删,1:已删\u0026#39; ); 创建struct 1 2 3 4 5 6 7 8 9 type Todo struct { TodoID int `db:\u0026#34;todo_id\u0026#34; json:\u0026#34;todo_id,omitempty\u0026#34;` Title string `db:\u0026#34;title\u0026#34; json:\u0026#34;title,omitempty\u0026#34;` Content string `db:\u0026#34;content\u0026#34; json:\u0026#34;content,omitempty\u0026#34;` UserID int `db:\u0026#34;user_id\u0026#34; json:\u0026#34;user_id,omitempty\u0026#34;` CreatedAt string `db:\u0026#34;created_at\u0026#34; json:\u0026#34;created_at,omitempty\u0026#34;` UpdatedAt string `db:\u0026#34;updated_at\u0026#34; json:\u0026#34;updated_at,omitempty\u0026#34;` IsDeleted bool `db:\u0026#34;is_deleted\u0026#34; json:\u0026#34;is_deleted,omitempty\u0026#34;` } 封装方法 此处以增删为例。封装常用方法是为了复用,封装时候使用害羞的代码。不需要为了封装而封装。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 // 新增Todo func (t *Todo) Add() (todoID int, err error) { // 连接数据库 db := database.DBConnect() defer db.Close() // 执行添加sql tx := db.MustBegin() result := tx.MustExec(\u0026#34;insert into todo (title, content, user_id) value (?, ?, ?)\u0026#34;, t.Title, t.Content, t.UserID) lastTodoID, err := result.LastInsertId() if err != nil { return } _ = tx.Commit() todoID = int(lastTodoID) return } // 删除Todo func (t *Todo) Del() { db := database.DBConnect() defer db.Close() tx := db.MustBegin() tx.MustExec(\u0026#34;update todo set is_deleted = 0 where is_deleted = 0 and todo_id = ?\u0026#34;, t.TodoID) _ = tx.Commit() } 视图中使用 此处以新增接口为例。\n调用封装的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package views import ( \u0026#34;github.com/gin-gonic/gin\u0026#34; \u0026#34;testGin/models\u0026#34; ) // addTodo.go -- post func AddTodo(c *gin.Context) { // {\u0026#34;title\u0026#34;: \u0026#34;hello\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;world\u0026#34;} var requestBody struct { Title string `json:\u0026#34;title\u0026#34;` Content string `json:\u0026#34;content\u0026#34;` } if c.ShouldBind(\u0026amp;requestBody) != nil { c.JSON(200, gin.H{ \u0026#34;code\u0026#34;: 40000, \u0026#34;message\u0026#34;: \u0026#34;参数有误\u0026#34;, }) return } todo := models.Todo{ Title: requestBody.Title, Content: requestBody.Content, UserID: 1, // 此处的1为假数据,此处应当从上下文获取请求用户的user_id } todoID, err := todo.Add() if err != nil { c.JSON(200, gin.H{ \u0026#34;code\u0026#34;: 20001, }) return } c.JSON(200, gin.H{ \u0026#34;code\u0026#34;: 20000, \u0026#34;todo_id\u0026#34;: todoID, }) } 直接使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package views import ( \u0026#34;github.com/gin-gonic/gin\u0026#34; \u0026#34;testGin/database\u0026#34; ) // addTodo.go -- post func AddTodo(c *gin.Context) { // {\u0026#34;title\u0026#34;: \u0026#34;hello\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;world\u0026#34;} var requestBody struct { Title string `json:\u0026#34;title\u0026#34;` Content string `json:\u0026#34;content\u0026#34;` } if c.ShouldBind(\u0026amp;requestBody) != nil { c.JSON(200, gin.H{ \u0026#34;code\u0026#34;: 40000, \u0026#34;message\u0026#34;: \u0026#34;参数有误\u0026#34;, }) return } // 连接数据库 db := database.DBConnect() defer db.Close() // 执行添加sql tx := db.MustBegin() result := tx.MustExec(\u0026#34;insert into todo (title, content, user_id) value (?, ?, ?)\u0026#34;, requestBody.Title, requestBody.Content, 1) lastTodoID, err := result.LastInsertId() if err != nil { c.JSON(200, gin.H{ \u0026#34;code\u0026#34;: 20001, }) return } _ = tx.Commit() c.JSON(200, gin.H{ \u0026#34;code\u0026#34;: 20000, \u0026#34;todo_id\u0026#34;: int(lastTodoID), }) } ","date":"2020-12-25T12:51:12Z","permalink":"https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/","title":"sqlx建模、连接与使用"},{"content":"前言 gin的中间件的使用场景非常广泛,此处主要介绍如何使用其来完成常见场景下的鉴权。\n官方文档 官方文档列出了如下几种使用方式:\n使用中间件 定制中间件 使用基础认证的中间件 中间件使用协程 不同场景的鉴权实现 api key 对于api key的方式需要设置白名单,对白名单外的请求进行token检测。此中间件在处理请求被处理之前对请求进行拦截,验证token,因此可在此处利用gin.Context来设置上下文,如请求所属用户的用户信息等。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package middleware import ( \u0026#34;fmt\u0026#34; \u0026#34;net/url\u0026#34; \u0026#34;strings\u0026#34; \u0026#34;github.com/gin-gonic/gin\u0026#34; ) func whiteList() map[string]string { return map[string]string{ \u0026#34;/ping\u0026#34;: \u0026#34;GET\u0026#34;, } } func withinWhiteList(url *url.URL, method string) bool { target := whiteList() queryUrl := strings.Split(fmt.Sprint(url), \u0026#34;?\u0026#34;)[0] if _, ok := target[queryUrl]; ok { if target[queryUrl] == method { return true } return false } return false } func Authorize() gin.HandlerFunc { return func(c *gin.Context) { type QueryToken struct { Token string `binding:\u0026#34;required,len=3\u0026#34; form:\u0026#34;token\u0026#34;` } // 当路由不在白名单内时进行token检测 if !withinWhiteList(c.Request.URL, c.Request.Method) { var queryToken QueryToken if c.ShouldBindQuery(\u0026amp;queryToken) != nil { c.AbortWithStatusJSON(200, gin.H{ \u0026#34;code\u0026#34;: 40001, }) return } c.Set(\u0026#34;role\u0026#34;, \u0026#34;user\u0026#34;) } c.Next() } } 路由权限 1)说明 对于请求的处理,需要去验证是否对其请求的路径拥有访问权限。\n首先看一下gin的路由设置:\n1 func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes 其参数为...HandlerFunc,其解释为:\n1 2 type HandlerFunc func(*Context) HandlerFunc defines the handler used by gin middleware as return value. 所以此处可以通过定制中间件的方式实现一个路由权限处理。\n当然此处的权限处理比较简单,使用角色直接去判断权限。如分为两个角色,管理员admin和普通用户user。\n不过此处实现有个前提条件,就是如何拿到用户的角色呢?此处需要在上一步(api key)的实现中加上利用gin.Context设置角色:\n1 c.Set(\u0026#34;role\u0026#34;, \u0026#34;admin\u0026#34;) // 可见上一步的代码,当然此处只是为了演示设置固定值 然后在中间件中拿到角色并进行判断。\n2)路由权限中间件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package middleware import ( \u0026#34;errors\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;github.com/gin-gonic/gin\u0026#34; ) func Permissions(roles []string) gin.HandlerFunc { return func(c *gin.Context) { permissionsErr := func() error { // 获取上下文中的用户角色 roleValue, exists := c.Get(\u0026#34;role\u0026#34;) if !exists { return errors.New(\u0026#34;获取用户信息失败\u0026#34;) } role := fmt.Sprint(roleValue) // 判断请求的用户的角色是否属于设定角色 noAccess := true for i := 0; i \u0026lt; len(roles); i++ { if role == roles[i] { noAccess = false } } if noAccess { return errors.New(\u0026#34;权限不够\u0026#34;) } return nil }() if permissionsErr != nil { c.AbortWithStatusJSON(200, gin.H{ \u0026#34;code\u0026#34;: 40001, }) return } c.Next() } } 3)使用 在设置路由时候,添加该中间件,并设置白名单。\n1 2 r.POST(\u0026#34;/todo\u0026#34;, middleware.Permissions([]string{\u0026#34;admin\u0026#34;}), views.AddTodo) // 添加中间件将会验证角色 r.PUT(\u0026#34;/todo\u0026#34;, views.ModifyTodo) // 未添加中间件则不会验证角色 ","date":"2020-12-18T14:02:30Z","permalink":"https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/","title":"gin中间件和鉴权"},{"content":"前言 在数据量较小的情况下,可以使用Redis来实现消息的发布与订阅,来代替Kafka。Kafka对于数据量大的场景下性能卓越,但是对于如此小场景时候,不仅运维成本提升,还用不上多少性能。\n不过使用Redis的另一个弊端是消息不能堆积,一旦消费者节点没有消费消息,消息将会丢失。因此需要评估当下场景来选择适合的架构。\n此处使用go-redis来实现Redis的发布与订阅。\n官方文档 官方文档有较为完整的例子:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 pubsub := rdb.Subscribe(ctx, \u0026#34;mychannel1\u0026#34;) // Wait for confirmation that subscription is created before publishing anything. _, err := pubsub.Receive(ctx) if err != nil { panic(err) } // Go channel which receives messages. ch := pubsub.Channel() // Publish a message. err = rdb.Publish(ctx, \u0026#34;mychannel1\u0026#34;, \u0026#34;hello\u0026#34;).Err() if err != nil { panic(err) } time.AfterFunc(time.Second, func() { // When pubsub is closed channel is closed too. _ = pubsub.Close() }) // Consume messages. for msg := range ch { fmt.Println(msg.Channel, msg.Payload) } 代码实现 分步讲解下具体实现代码。\n连接redis 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func redisConnect() (rdb *redis.Client) { var ( redisServer string port string password string ) redisServer = os.Getenv(\u0026#34;RedisUrl\u0026#34;) port = os.Getenv(\u0026#34;RedisPort\u0026#34;) password = os.Getenv(\u0026#34;RedisPass\u0026#34;) rdb = redis.NewClient(\u0026amp;redis.Options{ Addr: redisServer + \u0026#34;:\u0026#34; + port, Password: password, DB: 0, // use default DB }) return } 发布消息 1 2 3 4 func pubMessage(channel, msg string) { rdb := redisConnect() rdb.Publish(context.Background(), channel, msg) } 订阅消息 1 2 3 4 5 6 7 8 9 10 11 12 13 func subMessage(channel string) { rdb := redisConnect() pubsub := rdb.Subscribe(context.Background(), channel) _, err := pubsub.Receive(context.Background()) if err != nil { panic(err) } ch := pubsub.Channel() for msg := range ch { fmt.Println(msg.Channel, msg.Payload) } } 完整案例 此处分为一个发布节点和一个订阅节点来实现了简单的发布与订阅。\n消息发布节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package main import ( \u0026#34;context\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;github.com/go-redis/redis/v8\u0026#34; ) func redisConnect() (rdb *redis.Client) { var ( redisServer string port string password string ) redisServer = os.Getenv(\u0026#34;RedisUrl\u0026#34;) port = os.Getenv(\u0026#34;RedisPort\u0026#34;) password = os.Getenv(\u0026#34;RedisPass\u0026#34;) rdb = redis.NewClient(\u0026amp;redis.Options{ Addr: redisServer + \u0026#34;:\u0026#34; + port, Password: password, DB: 0, // use default DB }) return } func pubMessage(channel, msg string) { rdb := redisConnect() rdb.Publish(context.Background(), channel, msg) } func main() { channel := \u0026#34;hello\u0026#34; msgList := []string{\u0026#34;hello\u0026#34;, \u0026#34;world\u0026#34;} // 此处发了两个消息 for _, msg := range msgList { pubMessage(channel, msg) fmt.Printf(\u0026#34;已经发送%s到%s\\n\u0026#34;, msg, channel) } } 消息订阅节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package main import ( \u0026#34;context\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;github.com/go-redis/redis/v8\u0026#34; ) func redisConnect() (rdb *redis.Client) { var ( redisServer string port string password string ) redisServer = os.Getenv(\u0026#34;RedisUrl\u0026#34;) port = os.Getenv(\u0026#34;RedisPort\u0026#34;) password = os.Getenv(\u0026#34;RedisPass\u0026#34;) rdb = redis.NewClient(\u0026amp;redis.Options{ Addr: redisServer + \u0026#34;:\u0026#34; + port, Password: password, DB: 0, // use default DB }) return } func subMessage(channel string) { rdb := redisConnect() pubsub := rdb.Subscribe(context.Background(), channel) _, err := pubsub.Receive(context.Background()) if err != nil { panic(err) } ch := pubsub.Channel() for msg := range ch { fmt.Println(msg.Channel, msg.Payload) } } func main() { channel := \u0026#34;hello\u0026#34; subMessage(channel) } 运行结果 消息发布节点输出 消息订阅节点输出 ","date":"2020-11-27T17:41:53Z","permalink":"https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/","title":"go-Redis的发布与订阅"},{"content":"前言 想要写出好的 Go 程序,单元测试是很重要的一部分。 testing 包为提供了编写单元测试所需的工具,写好单元测试后,可以通过 go test 命令运行测试。\n规则 testing 为 Go 语言 package 提供自动化测试的支持。通过 go test 命令,能够自动执行如下形式的任何函数:\n1 func TestXxx(*testing.T) 要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 TestXxx 函数,如上所述。 将该文件放在与被测试文件相同的包中。该文件将被排除在正常的程序包之外,但在运行 go test 命令时将被包含。\n代码结构 1 2 3 4 5 . ├── go.mod ├── intMinBasicDriven_test.go ├── intMinBasic_test.go └── main.go 第一个单元测试 要被测试的代码 1 2 3 4 5 6 7 8 9 10 11 // main.go package main func IntMin(a, b int) int { // 返回a与b中的较小值 if a \u0026lt; b { return a } else { return b } } 测试代码 1 2 3 4 5 6 7 8 9 10 11 12 13 // intMinBasic_test.go package main import \u0026#34;testing\u0026#34; func TestIntMinBasic(t *testing.T) { ans := IntMin(2, -2) if ans != -2 { // t.Error* 会报告测试失败的信息,然后继续运行测试。 // t.Fail* 会报告测试失败的信息,然后立即终止测试。 t.Errorf(\u0026#34;IntMin(2, -2) = %d; want -2\u0026#34;, ans) } } 运行测试 1 2 3 go test // 输出 ok heihei\t0.385s Table-Driven Test 单元测试可以重复,所以会经常使用 表驱动 风格编写单元测试, 表中列出了输入数据,预期输出,使用循环,遍历并执行测试逻辑。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // intMinBasicDriven_test.go package main import ( \u0026#34;fmt\u0026#34; \u0026#34;testing\u0026#34; ) func TestIntMinTableDriven(t *testing.T) { var tests = []struct { a, b int want int }{ {0, 1, 0}, {1, 0, 0}, {2, -2, -2}, {0, -1, -1}, {-1, 0, -1}, } // t.Run 可以运行一个 “subtests” 子测试,一个子测试对应表中一行数据。 运行 go test -v 时,他们会分开显示。 for _, tt := range tests { testname := fmt.Sprintf(\u0026#34;%d,%d\u0026#34;, tt.a, tt.b) t.Run(testname, func(t *testing.T) { ans := IntMin(tt.a, tt.b) if ans != tt.want { t.Errorf(\u0026#34;got %d, want %d\u0026#34;, ans, tt.want) } }) } } 运行代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 go test -v // 输出 === RUN TestIntMinTableDriven === RUN TestIntMinTableDriven/0,1 === RUN TestIntMinTableDriven/1,0 === RUN TestIntMinTableDriven/2,-2 === RUN TestIntMinTableDriven/0,-1 === RUN TestIntMinTableDriven/-1,0 --- PASS: TestIntMinTableDriven (0.00s) --- PASS: TestIntMinTableDriven/0,1 (0.00s) --- PASS: TestIntMinTableDriven/1,0 (0.00s) --- PASS: TestIntMinTableDriven/2,-2 (0.00s) --- PASS: TestIntMinTableDriven/0,-1 (0.00s) --- PASS: TestIntMinTableDriven/-1,0 (0.00s) PASS ok heihei\t0.566s ","date":"2020-11-20T15:07:06Z","permalink":"https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/","title":"go单元测试"},{"content":"前言 Go 标准库专门提供了 net/http/httptest 包专门用于进行 http Web 开发测试。\n此处基于gin来实现http的单元测试。\nGET请求 此处使用http单元测试对/ping请求测试,其正常会返回字符串pong,响应码为200。\nweb应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package main func setupRouter() *gin.Engine { r := gin.Default() r.GET(\u0026#34;/ping\u0026#34;, func(c *gin.Context) { c.String(200, \u0026#34;pong\u0026#34;) }) return r } func main() { r := setupRouter() r.Run(\u0026#34;:8080\u0026#34;) } http单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package main import ( \u0026#34;net/http\u0026#34; \u0026#34;net/http/httptest\u0026#34; \u0026#34;testing\u0026#34; \u0026#34;github.com/stretchr/testify/assert\u0026#34; ) func TestPingRoute(t *testing.T) { router := setupRouter() // 创建http server并发起请求 w := httptest.NewRecorder() req, _ := http.NewRequest(\u0026#34;GET\u0026#34;, \u0026#34;/ping\u0026#34;, nil) router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) // 断言响应码为200 assert.Equal(t, \u0026#34;pong\u0026#34;, w.Body.String()) // 断言响应为\u0026#34;pong\u0026#34; } POST/PUT/DELETE请求 post、put、delete请求处理相似,都是处理其request body,因此此处只以post为例。\n此处对/todo进行测试,其正常返回为json,其中code为20000,响应码为200。\nweb应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package main func setupRouter() *gin.Engine { r := gin.Default() r.POST(\u0026#34;/todo\u0026#34;, func(c *gin.Context) { type ToDo struct { TodoId int `binding:\u0026#34;required\u0026#34; json:\u0026#34;todo_id\u0026#34;` } var todo ToDo if err := c.ShouldBindJSON(\u0026amp;todo); err != nil { c.JSON(400, gin.H{ \u0026#34;message\u0026#34;: \u0026#34;参数不全\u0026#34;, }) return } // 处理代码,此处以打印为例,省略处理... fmt.Println(todo.TodoId) c.JSON(200, gin.H{ \u0026#34;code\u0026#34;: 20000, }) }) return r } func main() { r := setupRouter() r.Run(\u0026#34;:8080\u0026#34;) } http单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package main func TestTodoCreate(t *testing.T) { // 请求方法 method := \u0026#34;POST\u0026#34; // 请求路由 urlStr := \u0026#34;/todo\u0026#34; // request body body := map[string]interface{}{ \u0026#34;title\u0026#34;: \u0026#34;hello\u0026#34;, } jsonByte, _ := json.Marshal(body) req := httptest.NewRequest(method, tc.urlStr, bytes.NewReader(jsonByte)) w := httptest.NewRecorder() router := routers.SetupRouter() router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) // 判断响应码 var response map[string]int json.Unmarshal([]byte(w.Body.String()), \u0026amp;response) value, exits := response[\u0026#34;code\u0026#34;] assert.True(t, exits) assert.Equal(t, 20000, value) // 判断自定义状态码 } 封装测试请求 由于http单元测试代码中存在较多重复,因此此处封装重复代码。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package tests type TestConfig struct { Url string Method string Body interface{} } func (tc *TestConfig) Request() *httptest.ResponseRecorder { var req *http.Request if tc.Body != nil { jsonByte, _ := json.Marshal(tc.Body) req = httptest.NewRequest(tc.Method, tc.Url, bytes.NewReader(jsonByte)) } else { req = httptest.NewRequest(tc.Method, tc.Url, nil) } w := httptest.NewRecorder() router := routers.SetupRouter() router.ServeHTTP(w, req) return w } 封装后的单元测试 此处以post请求为例。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package main func TestAddTag(t *testing.T) { // 新增tag var addTestConfig tests.TestConfig addTestConfig.Method = \u0026#34;POST\u0026#34; addTestConfig.Url = \u0026#34;/tag\u0026#34; addTestConfig.Body = map[string]string{ \u0026#34;tag_name\u0026#34;: \u0026#34;HelloHello\u0026#34;, } w := addTestConfig.Request() assert.Equal(t, 200, w.Code) var addResponse map[string]int json.Unmarshal([]byte(w.Body.String()), \u0026amp;addResponse) value, exits := addResponse[\u0026#34;code\u0026#34;] assert.True(t, exits) assert.Equal(t, 20000, value) lastInsertTagId, exits := addResponse[\u0026#34;tag_id\u0026#34;] assert.True(t, exits) } ","date":"2020-11-20T14:47:04Z","permalink":"https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/","title":"gin的http单元测试"},{"content":"前言 代码运行过程中,意外情况会导致500错误,对于使用者来说体验很不好,对于开发者来说也无法及时获取错误,需要去查看日志。\n并且有的插件在某些报错情况下会返回一些敏感信息,非常危险。因此需要去捕获全局错误,通知开发者,自定义错误消息等。\n实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from server import app from flask import request from datetime import datetime from werkzeug.exceptions import HTTPException @app.errorhandler(Exception) def all_exception_handler(e): if isinstance(e, HTTPException): if e.code == 404: return { \u0026#39;code\u0026#39;: 40004, \u0026#39;message\u0026#39;: \u0026#39;404\u0026#39; }, 404 # 通知开发者/写入日志 handle(path = request.path, content = str(e)) return { \u0026#39;code\u0026#39;: 20001, \u0026#39;message\u0026#39;: \u0026#39;Error\u0026#39; } ","date":"2020-11-02T20:51:02Z","permalink":"https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/","title":"Flask设置全局错误捕获"},{"content":"前言 当前产品遇到一个报错,就是接口收到请求没有限制请求字段长度,导致字段长度超过数据库对应字段长度,直接报了500。因此也对此有些新的需求,需要在后端限制请求字段最大长度。\n环境 开发语言:Python 3.7 后端框架:Flask 1.1.1 插件:Flask-RESTful 0.3.8 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 def field_max_limit(max_length): def validate(s): if type(s) != str: raise ValidationError(\u0026#34;The field must be String.\u0026#34;) if len(s) \u0026lt;= max_length: return s raise ValidationError(\u0026#34;The field cannot exceed %i characters.\u0026#34; % max_length) return validate # 解析请求参数时候验证长度 parse.add_argument(\u0026#39;username\u0026#39;, type = field_max_limit(5), required = True) 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from flask import Flask from flask_restful import Api, Resource, reqparse from werkzeug.routing import ValidationError app = Flask(__name__) api = Api(app) def field_max_limit(max_length): def validate(s): if type(s) != str: raise ValidationError(\u0026#34;The field must be String.\u0026#34;) if len(s) \u0026lt;= max_length: return s raise ValidationError(\u0026#34;The field cannot exceed %i characters.\u0026#34; % max_length) return validate class Login(Resource): def post(self): parse = reqparse.RequestParser() parse.add_argument(\u0026#39;username\u0026#39;, type = field_max_limit(5), required = True) parse.add_argument(\u0026#39;password\u0026#39;, type = field_max_limit(20), required = True) args = parse.parse_args() print({ \u0026#39;username\u0026#39;: args.username, \u0026#39;password\u0026#39;: args.password }) return { \u0026#39;code\u0026#39;: 20000 } api.add_resource(Login, \u0026#39;/login\u0026#39;) ","date":"2020-11-02T20:24:35Z","permalink":"https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/","title":"flask_restful限制request字段长度"},{"content":"前言 在使用Docker时,其默认时区并非使用者所在时区,需要进行修改。对于单个容器,当前修改有几种常见方式,比如直接映射宿主机时区到容器内,而本文介绍的为使用Dockerfile来直接修改镜像时区。此处仅以常见几个基础容器为例来介绍。\n常见容器 Alpine 1 2 3 4 5 6 7 FROM alpine:latest # 安装tzdata RUN apk add --no-cache tzdata # 设置时区 ENV TZ=\u0026#34;Asia/Shanghai\u0026#34; 验证 1 2 docker build -t alpine:time . docker run --rm -it alpine:time date Ubuntu 1 2 3 4 5 6 7 8 9 10 FROM ubuntu # 设置localtime # 此处需要优先设置localtime,否则安装tzdata将会进入时区选择 RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 安装tzdata RUN apt-get update \\ \u0026amp;\u0026amp; apt-get install tzdata -y \\ \u0026amp;\u0026amp; apt-get clean 验证 1 2 docker build -t ubuntu:time . docker run --rm -it ubuntu:time date Debian Debian中已经安装了tzdata,所以跟Ubuntu有所不通过 1 2 3 4 5 6 7 8 9 10 11 FROM debian # 修改设置dpkg为自动配置 ENV DEBIAN_FRONTEND=noninteractive RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN dpkg-reconfigure -f noninteractive tzdata # 修改设置dpkg为手动输入选择操作 ENV DEBIAN_FRONTEND=dialog 验证 1 2 docker build -t debian:time . docker run --rm -it debian:time date 结语 此处不再列举太多,主要解决方式为安装tzdata,然后修改时区。\n","date":"2020-10-14T23:21:39Z","permalink":"https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/","title":"Docker修改时区"},{"content":"简述 在特定敏感数据的场景需要加密,一开始采用rsa加密,但是rsa加密对性能要求较高,在解密时候对于数据量限制较大,导致加密传输的数据量上限较低。而采用Base64虽然简单明了但是解密过于简单。因此采用折中的对称加密aes。\n而aes加密需要前后端加密类型相同,因此此处采用CTR,其对加密文本没有长度限制。\n前端实现 1 2 3 4 5 6 7 let crypto = require(\u0026#34;crypto\u0026#34;) export function aesEncrypted(key, text) { let iv = Buffer.concat([ crypto.randomBytes(12), Buffer.alloc(4, 0) ]) let cipher = crypto.createCipheriv(\u0026#34;aes-128-ctr\u0026#34;, key, iv) return iv.toString(\u0026#39;hex\u0026#39;) + cipher.update(text, \u0026#39;utf8\u0026#39;, \u0026#39;hex\u0026#39;) + cipher.final(\u0026#39;hex\u0026#39;) } 后端实现 1 2 3 4 5 6 7 8 9 10 11 def aesDecryption(key_: str, de_text: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34; aes解密函数 :param key_: aes的key :param de_text: aes加密的密文 :return: 解密的文本 \u0026#34;\u0026#34;\u0026#34; ct = codecs.decode(de_text.encode(), \u0026#39;hex\u0026#39;) counter = Counter.new(32, prefix = ct[:12], initial_value = 0) cipher = AES.new(key_.encode(), AES.MODE_CTR, counter = counter) return cipher.decrypt(ct[16:]).decode() 示例代码 前端 1 2 3 4 5 6 7 let crypto = require(\u0026#34;crypto\u0026#34;) export function aesEncrypted(key, text) { let iv = Buffer.concat([ crypto.randomBytes(12), Buffer.alloc(4, 0) ]) let cipher = crypto.createCipheriv(\u0026#34;aes-128-ctr\u0026#34;, key, iv) return iv.toString(\u0026#39;hex\u0026#39;) + cipher.update(text, \u0026#39;utf8\u0026#39;, \u0026#39;hex\u0026#39;) + cipher.final(\u0026#39;hex\u0026#39;) } 后端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from base64 import b64encode from Crypto.Cipher import AES from Crypto.Util import Counter from random import randint import codecs import hashlib def convert_to_md5(info: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34; md5加密 :param info: 需要加密的内容 :return: md5加密密文 \u0026#34;\u0026#34;\u0026#34; md5 = hashlib.md5() md5.update(info.encode(\u0026#39;utf-8\u0026#39;)) return md5.hexdigest() def aesCreateKey() -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34; 生成aes加密的key,key的长度必须16位 :return: 返回key的base64密文 \u0026#34;\u0026#34;\u0026#34; en_key = convert_to_md5(str(randint(100000, 999999)))[8:-8] return b64encode(en_key.encode()).decode() def aesDecryption(key_: str, de_text: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34; aes解密函数 :param key_: aes的key :param de_text: aes加密的密文 :return: 解密的文本 \u0026#34;\u0026#34;\u0026#34; ct = codecs.decode(de_text.encode(), \u0026#39;hex\u0026#39;) counter = Counter.new(32, prefix = ct[:12], initial_value = 0) cipher = AES.new(key_.encode(), AES.MODE_CTR, counter = counter) return cipher.decrypt(ct[16:]).decode() 参考文档 https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto ","date":"2020-10-09T21:12:50Z","permalink":"https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/","title":"基于python3和js的前后端aes加解密"},{"content":"报错 1 an upstream response is buffered to a temporary file 解决 Nginx配置加上如下配置\n1 2 proxy_max_temp_file_size 0; client_max_body_size 50m; ","date":"2020-08-15T22:07:58Z","permalink":"https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/","title":"nginx报错an upstream response is buffered to a temporary file"},{"content":"前言 对于Golang操作Redis,此处使用github.com/go-redis/redis。\n操作 连接redis服务器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package redis import ( \u0026#34;context\u0026#34; \u0026#34;os\u0026#34; \u0026#34;time\u0026#34; \u0026#34;github.com/go-redis/redis/v8\u0026#34; jsoniter \u0026#34;github.com/json-iterator/go\u0026#34; ) var ctx = context.Background() func BaseClient() (rdb *redis.Client) { redisServer := \u0026#34;redis_server\u0026#34; port := \u0026#34;redis_port\u0026#34; password := \u0026#34;redis_password\u0026#34; rdb = redis.NewClient(\u0026amp;redis.Options{ Addr: redisServer + \u0026#34;:\u0026#34; + port, Password: password, DB: 0, }) return } ###存储\n1 2 3 4 5 6 func SetJson(key string, value map[string]interface{}, expiration int) (err error) { rdb := BaseClient() valueString, _ := jsoniter.MarshalToString(value) err = rdb.Set(ctx, key, valueString, time.Duration(expiration)*time.Second).Err() return } 调用 1 2 3 4 5 6 7 func main() { value, _ := redis.SetJson(\u0026#34;user\u0026#34;, map[string]interface{}{ \u0026#34;name\u0026#34;: \u0026#34;tom\u0026#34;, \u0026#34;age\u0026#34;: 12 }), 60, } ###读取\n1 2 3 4 5 func Get(key string) (value string, err error) { rdb := BaseClient() value, err = rdb.Get(ctx, key).Result() return } 调用 1 2 3 4 5 6 7 8 9 10 11 12 13 type User struct { Name\tstring Age\tint } func main() { value, _ := redis.Get(\u0026#34;user\u0026#34;) var user User json.Unmarshal([]byte(value), \u0026amp;user) fmt.Print(user) } 结语 使用JSON格式存储与读取其实就是对目标数据在存储前和读取后进行格式转换。\n","date":"2020-08-11T18:42:42Z","permalink":"https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/","title":"Golang使用JSON格式存取Redis"},{"content":" 1 2 3 4 5 6 7 8 9 10 11 12 13 package main import ( \u0026#34;math/rand\u0026#34; \u0026#34;strconv\u0026#34; ) func CreateVerifyCode() (verifyCode string) { min := 100000 max := 999999 verifyCode = strconv.Itoa(rand.Intn(max-min) + min) return } ","date":"2020-08-11T09:08:20Z","permalink":"https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/","title":"Go生成6位随机数"},{"content":"报错 按照ElementUI官方文档按需引入却报错,首先报错缺少babel-preset-es2015。安装该组件之后编译却报错。\n1 Error: Plugin/Preset files are not allowed to export objects, only functions. 解决 该问题为babel版本冲突。\n安装插件 1 yarn add @babel/preset-env 编辑.babelrc 1 2 3 4 5 6 7 8 9 10 11 12 { \u0026#34;presets\u0026#34;: [[\u0026#34;@babel/preset-env\u0026#34;, { \u0026#34;modules\u0026#34;: false }]], \u0026#34;plugins\u0026#34;: [ [ \u0026#34;component\u0026#34;, { \u0026#34;libraryName\u0026#34;: \u0026#34;element-ui\u0026#34;, \u0026#34;styleLibraryName\u0026#34;: \u0026#34;theme-chalk\u0026#34; } ] ] } ","date":"2020-08-10T00:00:54Z","permalink":"https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/","title":"Vue按需引入ElementUI报错Error -- Plugin -- Preset files are not allowed to export objects, only functions"},{"content":"前言 Flask_RESTful是一个Flask 扩展,它添加了快速构建 REST APIs 的支持。其请求解析接口是模仿 argparse 接口。它设计成提供简单并且统一的访问 Flask 中 flask.request 对象里的任何变量的入口。\n常见类型解析 基本参数 请求 1 2 3 4 { \u0026#34;username\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;info\u0026#34;: \u0026#34;heihei\u0026#34; } 解析 1 2 3 4 parse = reqparse.RequestParser() parse.add_argument(\u0026#39;username\u0026#39;, type = str) parse.add_argument(\u0026#39;info\u0026#39;, type = str) args = parse.parse_args() 必选参数 使用参数required。\n请求 1 2 3 4 { \u0026#34;username\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;info\u0026#34;: \u0026#34;heihei\u0026#34; } 解析 1 2 3 4 parse = reqparse.RequestParser() parse.add_argument(\u0026#39;username\u0026#39;, type = str, required = True) parse.add_argument(\u0026#39;info\u0026#39;, type = str, required = True) args = parse.parse_args() 列表[string] 使用参数action = 'append'\n请求 1 2 3 4 5 6 { \u0026#34;username\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;info\u0026#34;: [ \u0026#34;handsome\u0026#34;, \u0026#34;cheerful\u0026#34;, \u0026#34;optimism\u0026#34; ] } 解析 1 2 3 4 parse = reqparse.RequestParser() parse.add_argument(\u0026#39;username\u0026#39;, type = str, required = True) parse.add_argument(\u0026#39;info\u0026#39;, type = str, action = \u0026#39;append\u0026#39;, required = True) args = parse.parse_args() 列表[dict] 使用参数action = 'append'\n请求 1 2 3 4 5 6 7 8 9 10 11 12 13 { \u0026#34;username\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;friends\u0026#34;: [ { \u0026#34;username\u0026#34;: \u0026#34;tom\u0026#34;, \u0026#34;age\u0026#34;: 20 }, { \u0026#34;username\u0026#34;: \u0026#34;jerry\u0026#34;, \u0026#34;age\u0026#34;: 20 } ] } 解析 1 2 3 4 parse = reqparse.RequestParser() parse.add_argument(\u0026#39;username\u0026#39;, type = str, required = True) parse.add_argument(\u0026#39;info\u0026#39;, type = dict, action = \u0026#39;append\u0026#39;, required = True) args = parse.parse_args() JSON 请求 1 2 3 4 5 6 7 { \u0026#34;username\u0026#34;: \u0026#34;kuari\u0026#34;, \u0026#34;info\u0026#34;: { \u0026#34;character\u0026#34;: \u0026#34;optimism\u0026#34;, \u0026#34;age\u0026#34;: 20 } } 解析 1 2 3 4 parse = reqparse.RequestParser() parse.add_argument(\u0026#39;username\u0026#39;, type = str, required = True) parse.add_argument(\u0026#39;info\u0026#39;, type = dict, required = True) args = parse.parse_args() ","date":"2020-08-05T15:49:56Z","permalink":"https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/","title":"Flask_RESTful解析常见类型请求数据"},{"content":"报错 在Go中POST请求时报错\n1 x509: certificate signed by unknown authority 即无法检验证书。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package main import ( \u0026#34;net/http\u0026#34; ) func Handle() { ... _, err := http.Post( ... ) ... } 解决 跳过校验即可。此处引入\u0026quot;crypto/tls\u0026quot;。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package main import ( \u0026#34;crypto/tls\u0026#34; \u0026#34;net/http\u0026#34; ) func Handle() { ... tr := \u0026amp;http.Transport{ TLSClientConfig: \u0026amp;tls.Config{InsecureSkipVerify: true}, } client := \u0026amp;http.Client{ Timeout: 15 * time.Second, Transport: tr, } _, err := client.Post( ... ) ... } ","date":"2020-08-04T09:58:55Z","permalink":"https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/","title":"Go http请求报错x509 certificate signed by unknown authority"},{"content":"前言 部署SFTP服务器,数次遇到几个报错,特此记录\n环境 路径\n1 2 3 home └── tom └── uploads 用户为tom\n报错 报错一 1 permission denied 报错二 1 bad ownership or modes for chroot directory component \u0026#34;/home\u0026#34; 解决 以上两个报错,此处为统一解决。\n创建用户组 1 groupadd ftp 将用户加入用户组 1 usermod -a -G ftp tom 设置权限 1 2 3 chown root:ftp -R /home/tom chown tom:ftp -R /home/tom/uploads chmod 755 -R /home ","date":"2020-07-23T16:56:30Z","permalink":"https://blog.hunterji.com/p/sftp%E9%83%A8%E7%BD%B2%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E8%AE%B0%E5%BD%95/","title":"SFTP部署报错解决记录"},{"content":"一对多 1 2 3 4 5 6 7 8 9 10 class Country(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(30), unique=True) capital = db.relationship(\u0026#39;Capital\u0026#39;, uselist=False) class Capital(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(30), unique=True) country_id = db.Column(db.Integer, db.ForeignKey(\u0026#39;country.id\u0026#39;)) country = db.relationship(\u0026#39;Country\u0026#39;) 一对多 1 2 3 4 5 6 7 8 9 class Author(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(70), unique=True) phone = db.Column(db.String(20)) class Article(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(50), index=True) body =\tdb.Column(db.Text) 多对一 1 2 3 4 5 6 7 8 9 class Citizen(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(70), unique=True) city_id = db.Column(db.Integer, db.ForeignKey(\u0026#39;city.id\u0026#39;)) city = db.relationship(\u0026#39;City\u0026#39;) class City(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(30), unique=True) 多对多 1 2 3 4 5 6 7 8 9 10 11 12 13 association_table = db.Table(\u0026#39;association\u0026#39;,db.Column(\u0026#39;student_id\u0026#39;, db.Integer, db.Foreign) class Student(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(70), unique=True) grade = db.Column(db.String(20)) teachers = db.relationship(\u0026#39;Teacher\u0026#39;, secondary=association_table, back_populates=\u0026#39;students\u0026#39;) class Teacher(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(70), unique=True) office = db.Column(db.String(20)) ","date":"2020-06-23T13:46:21Z","permalink":"https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/","title":"flask_sqlalchemy一对一、一对多、多对一、多对多"},{"content":"问题 使用docker运行的wordpress,有报错The uploaded file exceeds the upload_max_filesize directive in php.ini。\n过程 按照一般的方式去修改文件php.ini:\n1 2 upload_max_filesize = 30M post_max_size = 30M 容器中有两个php.ini文件:\nphp.ini-development php.ini-production 都修改之后却并不生效。\n解决 在文件夹conf.d中添加文件uploads.ini:\n1 2 3 4 5 file_uploads = On memory_limit = 64M upload_max_filesize = 64M post_max_size = 64M max_execution_time = 600 文件夹conf.d位置需要看具体环境:\n1 php -i | grep php.ini 然后在其目录下找到文件夹conf.d。\n也可以通过在运行容器时候直接将该文件映射进入。\n","date":"2020-06-03T15:35:33Z","permalink":"https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/","title":"docker中php上传大小限制"},{"content":"需求 Linux对于开发者来说极其友好,但是由于国内主流办公产品相关的生态较为匮乏,因此如何使用Linux去分享文件是一件十分头疼的问题。\n对于这个问题,可以直接使用静态文件服务器解决部分需求,如下介绍几个常见方法。\n语言类 Python 对于Python来说,可以直接使用内置的库来实现。\npython2\n1 python -m SimpleHTTPServer 8000 Python3\n1 python -m http.server 8000 Node.js node生态内有一个项目http-server,直接V8引擎带你飞。\n安装 Npm 1 npm install --global http-server Homebrew 1 brew install http-server 运行 1 http-server [path] [options] 例如:\n1 2 cd exmaple/ http-server 项目仓库地址 https://github.com/http-party/http-server\n服务类 Nginx/Apache Nginx和Apache本身可用于静态文件服务器,这就需要用户直接在本地安装。\n当然,nginx需要注意配置一下,打开索引:\n1 2 3 4 5 6 7 8 9 server { listen\t80; ... location / { root /usr/share/nginx/html; autoindex on; } } Docker 使用Docker其实也是使用如Nginx来实现静态文件服务器,但是容器化在该场景存在几大优势:\n即开即用 环境隔离 相对于直接安装Nginx或者Apache,更推荐使用Docker。\n","date":"2020-06-02T19:05:56Z","permalink":"https://blog.hunterji.com/p/linux%E5%88%86%E4%BA%AB%E6%96%87%E4%BB%B6%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BA%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/","title":"Linux分享文件?快速创建静态文件服务器"},{"content":"介绍 为了前后端传输数据的安全性,需要对数据进行加密。因此选定使用非对称加密,此处为RSA。\n在传输数据前,后端生成公钥和私钥,将公钥给前端,前端加密之后,将密文传给后端,后端使用私钥解密即可得到原始的数据。\n环境 前端使用jsencrypt加密 后端使用Crypto生成密钥和解密 后端生成密钥 1 2 3 4 5 6 7 8 9 10 11 12 from Crypto.PublicKey import RSA def generate_key(): \u0026#34;\u0026#34;\u0026#34; 生成公钥和私钥 :return: 返回私钥和公钥 \u0026#34;\u0026#34;\u0026#34; rsa = RSA.generate(1024) private_key = rsa.exportKey() publick_key = rsa.publickey().exportKey() return private_key.decode(), publick_key.decode() 前端用公钥加密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { JSEncrypt } from \u0026#34;jsencrypt\u0026#34;; function rsa_en(pubkey, target_str) { /** 分段加密信息 :params target_str: 需要加密的信息,此处为很长的信息 :pubkey: 公钥 :return: 存储密文的数组 **/ let encrypt = new JSEncrypt(); encrypt.setPublicKey(pubkey); let result = encrypt.encrypt(JSON.stringify(target_str)); } 后端使用私钥解密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 from Crypto.Hash import SHA from Crypto import Random from base64 import b64decode def rsa_decrypt(private_key, message): \u0026#34;\u0026#34;\u0026#34; rsa解密函数 :prams private_key: 私钥 :params message: 加密后的密文 :return: 解密后原始信息 \u0026#34;\u0026#34;\u0026#34; dsize = SHA.digest_size sentinel = Random.new().read(1024 + dsize) private_key = RSA.import_key(private_key) cipher_rsa = PKCS1_v1_5.new(private_key) return cipher_rsa.decrypt(b64decode(message), sentinel) 这里使用base64先解密一遍是必要的,否则报错ValueError: Ciphertext with incorrect length. 完整代码 前端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { JSEncrypt } from \u0026#34;jsencrypt\u0026#34;; function rsa_en(pubkey, target_str) { /** 分段加密信息 :params target_str: 需要加密的信息,此处为很长的信息 :pubkey: 公钥 :return: 存储密文的数组 **/ let encrypt = new JSEncrypt(); encrypt.setPublicKey(pubkey); let result = encrypt.encrypt(JSON.stringify(target_str)); } 后端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 from Crypto.Hash import SHA from Crypto import Random from base64 import b64decode def generate_key(): \u0026#34;\u0026#34;\u0026#34; 生成公钥和私钥 :return: 返回私钥和公钥 \u0026#34;\u0026#34;\u0026#34; rsa = RSA.generate(1024) private_key = rsa.exportKey() publick_key = rsa.publickey().exportKey() return private_key.decode(), publick_key.decode() def rsa_encrypt(public_key, message): \u0026#34;\u0026#34;\u0026#34; rsa加密函数 :params publick_key: 公钥 :params message: 需要加密的信息 :return: 加密后的密文 \u0026#34;\u0026#34;\u0026#34; public_key = RSA.import_key(public_key) cipher_rsa = PKCS1_v1_5.new(public_key) return cipher_rsa.encrypt(str.encode(message)) def rsa_decrypt(private_key, message): \u0026#34;\u0026#34;\u0026#34; rsa解密函数 :prams private_key: 私钥 :params message: 加密后的密文 :return: 解密后原始信息 \u0026#34;\u0026#34;\u0026#34; dsize = SHA.digest_size sentinel = Random.new().read(1024 + dsize) private_key = RSA.import_key(private_key) cipher_rsa = PKCS1_v1_5.new(private_key) return cipher_rsa.decrypt(b64decode(message), sentinel) 分段加密 RSA加密信息最长为128位,过长则会报错,因此,对于过长的信息需要分段加密,后端也要分段解密后拼装。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import { JSEncrypt } from \u0026#34;jsencrypt\u0026#34;; function en_str(target_str, pubkey) { /** 分段加密信息 :params target_str: 需要加密的信息,此处为很长的信息 :pubkey: 公钥 :return: 存储密文的数组 **/ let encrypt = new JSEncrypt(); encrypt.setPublicKey(pubkey); let en_array = []; let n = 100; // 每段信息的长度 for (let i = 0, l = target_str.length; i \u0026lt; l / n; i++) { let message = target_str.slice(n * i, n * (i + 1)); en_array.push(encrypt.encrypt(message)); } return en_array; } ","date":"2020-05-22T15:11:05Z","permalink":"https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/","title":"vue+flask前后端信息传输非对称加密"},{"content":"场景 使用 v-charts 做数据可视化,需要给图表添加标题。\n解决方法 v-charts本身并没有提供显示标题的配置,顾需要引入 echarts 的 title 。\n实现 引入title 1 import \u0026#34;echarts/lib/component/title\u0026#34;; 添加标题配置 1 2 3 4 5 6 7 this.chartTitle = { text: \u0026#34;平台用户与创客数量对比图\u0026#34;, textStyle: { fontWeight: 600, color: \u0026#34;white\u0026#34; } }; 使用 1 \u0026lt;ve-bar :title=\u0026#34;chartTitle\u0026#34; /\u0026gt; 完整实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 \u0026lt;template\u0026gt; \u0026lt;ve-bar ... :title=\u0026#34;chartTitle\u0026#34; /\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; import \u0026#34;echarts/lib/component/title\u0026#34;; export default { data() { this.chartTitle = { text: \u0026#34;平台用户与创客数量对比图\u0026#34;, textStyle: { fontWeight: 600, color: \u0026#34;white\u0026#34; } }; } }; \u0026lt;/script\u0026gt; echarts配置手册 https://www.echartsjs.com/zh/option.html#title 参考文章 https://github.com/ElemeFE/v-charts/issues/191 ","date":"2020-03-26T14:54:27Z","permalink":"https://blog.hunterji.com/p/v-charts%E6%B7%BB%E5%8A%A0%E5%9B%BE%E8%A1%A8%E6%A0%87%E9%A2%98/","title":"v-charts添加图表标题"},{"content":"在vue2中不允许子组件直接修改props,为单项数据流,所有若要修改只能通过额外的值,并监听props以改变额外的值。\n设置props 1 2 3 4 5 6 props: { dialog: { type: Boolean, default: false } } 创建额外的值 在data中创建一个localDialog,其值为this.dialog。\n1 2 3 4 5 data() { return { localDialog: this.dialog } } 监听 保持同步的关键在于需要在子组件内监听props,即此处的dialog。\n1 2 3 4 5 watch: { dialog(val) { this.localDialog = val } } 子组件向父组件传递 子组件使用this.$emit()即可向父组件传递变化的值。\n1 2 3 4 5 methods: { sendToFather() { this.$emit(\u0026#39;dialogchange\u0026#39;, this.localDialog) } } 父组件调用 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;your-component :dialog=\u0026#34;dialog\u0026#34; @dialogchange=\u0026#34;dialogchange\u0026#34; /\u0026gt; data() { return { dialog: false } }, methods: { dialogchange(val) { this.dialog = val } } 完整代码 子组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 \u0026lt;template\u0026gt; \u0026lt;div :visible=\u0026#34;localDialog\u0026#34;\u0026gt; hunterji.com \u0026lt;button @click=\u0026#34;sendToFather\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; export default { props: { dialog: { type: Boolean, default: false } }, data() { return { localDialog: this.dialog } }, watch: { dialog(val) { this.localDialog = val } }, methods: { sendToFather() { this.$emit(\u0026#39;dialogchange\u0026#39;, this.localDialog) } } } \u0026lt;/script\u0026gt; 父组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 \u0026lt;template\u0026gt; \u0026lt;your-component :dialog=\u0026#34;dialog\u0026#34; @dialogchange=\u0026#34;dialogchange\u0026#34; /\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; import yourComponent from \u0026#39;./yourComponent\u0026#39; export default { components: { yourComponent }, data() { return { dialog: false } }, methods: { dialogchange(val) { this.dialog = val } } } \u0026lt;/script\u0026gt; ","date":"2020-03-21T23:48:37Z","permalink":"https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/","title":"vue组件props双向绑定"},{"content":"增 1 2 3 article = Article(title=\u0026#39;article1\u0026#39;, content=\u0026#39;heihei\u0026#39;) db.session.add(article) db.session.commit() 若要在新增之后获取数据库中新增数据的信息,如id。\n1 2 3 4 5 6 7 8 article = Article(title=\u0026#39;article1\u0026#39;, content=\u0026#39;heihei\u0026#39;) db.session.add(article) db.session.flush() # 添加这一条,用于预提交 db.session.commit() # 输出新增数据的信息 print(article.id) print(article.title) 删 1 2 3 4 5 6 7 8 ##把需要删除的数据查找出来 article = Article.query.filter_by(content = \u0026#39;heihei\u0026#39;).first() ##把这条数据删除掉 db.session.delete(article) ##提交 db.session.commit() 改 1 2 3 4 5 6 7 8 ##先把你要更改的数据查找出来 article = Article.query.filter(Article.title == \u0026#39;article1\u0026#39;).first() ##把这条数据需要修改的地方进行修改 article.title = \u0026#39;article2\u0026#39; ##提交 db.session.commit() 查 查询单个 1 2 3 4 5 article1 = Article.query.filter(Article.title == \u0026#39;article\u0026#39;).first() article1 = Article.query.filter_by(title = \u0026#39;article\u0026#39;).first() # 或者 print(article1.title) print(article1.content) 查询所有 1 2 3 4 article1 = Article.query.filter_by(title = \u0026#39;article\u0026#39;).all() for item in article1: print(item.title) print(item.content) 倒叙 1 article1 = Article.query.filter_by(title = \u0026#39;article\u0026#39;).order_by(Article.id.desc()).all() 限制数量 1 article1 = Article.query.filter_by(title = \u0026#39;article\u0026#39;).limit(10).all() ","date":"2020-03-20T12:19:52Z","permalink":"https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/","title":"Flask_sqlalchemy的增删改查"},{"content":"场景 前端导入excel表格,直接前端解析文件,将数据传给后端。\n需要的库 安装 1 npm install xlsx 使用 1 import XLSX from \u0026#34;xlsx\u0026#34;; 代码实现 html部分 1 2 3 4 5 6 7 8 9 10 \u0026lt;div class=\u0026#34;container\u0026#34;\u0026gt; {{ upload_file || \u0026#34;导入\u0026#34; }} \u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;.xls,.xlsx\u0026#34; class=\u0026#34;upload_file\u0026#34; @change=\u0026#34;readExcel($event)\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; JS部分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 readExcel(e) { // 读取表格文件 let that = this; const files = e.target.files; if (files.length \u0026lt;= 0) { return false; } else if (!/\\.(xls|xlsx)$/.test(files[0].name.toLowerCase())) { this.$message({ message: \u0026#34;上传格式不正确,请上传xls或者xlsx格式\u0026#34;, type: \u0026#34;warning\u0026#34; }); return false; } else { // 更新获取文件名 that.upload_file = files[0].name; } const fileReader = new FileReader(); fileReader.onload = ev =\u0026gt; { try { const data = ev.target.result; const workbook = XLSX.read(data, { type: \u0026#34;binary\u0026#34; }); const wsname = workbook.SheetNames[0]; //取第一张表 const ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]); //生成json表格内容 console.log(ws); } catch (e) { return false; } }; fileReader.readAsBinaryString(files[0]); } 整体代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026#34;container\u0026#34;\u0026gt; {{ upload_file || \u0026#34;导入\u0026#34; }} \u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;.xls,.xlsx\u0026#34; class=\u0026#34;upload_file\u0026#34; @change=\u0026#34;readExcel($event)\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; import XLSX from \u0026#34;xlsx\u0026#34;; export default { data() { return { upload_file: \u0026#34;\u0026#34;, lists: [] }; }, methods: { submit_form() { // 给后端发送请求,更新数据 console.log(\u0026#34;假装给后端发了个请求...\u0026#34;); }, readExcel(e) { // 读取表格文件 let that = this; const files = e.target.files; if (files.length \u0026lt;= 0) { return false; } else if (!/\\.(xls|xlsx)$/.test(files[0].name.toLowerCase())) { this.$message({ message: \u0026#34;上传格式不正确,请上传xls或者xlsx格式\u0026#34;, type: \u0026#34;warning\u0026#34; }); return false; } else { // 更新获取文件名 that.upload_file = files[0].name; } const fileReader = new FileReader(); fileReader.onload = ev =\u0026gt; { try { const data = ev.target.result; const workbook = XLSX.read(data, { type: \u0026#34;binary\u0026#34; }); const wsname = workbook.SheetNames[0]; //取第一张表 const ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]); //生成json表格内容 that.lists = []; // 从解析出来的数据中提取相应的数据 ws.forEach(item =\u0026gt; { that.lists.push({ username: item[\u0026#34;用户名\u0026#34;], phone_number: item[\u0026#34;手机号\u0026#34;] }); }); // 给后端发请求 this.submit_form(); } catch (e) { return false; } }; fileReader.readAsBinaryString(files[0]); } } }; \u0026lt;/script\u0026gt; 样式 原本的文件上传样式可能会跟页面整体风格不搭,所以需要修改其样式。不过此处并不是直接修改其样式而是通过写一个div来覆盖原有的上传按钮。此处样式与element UI中的primary按钮样式相同。\n实现该样式的关键在于.upload_file的opacity和position。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 .container { border: none; border-radius: 4px; background-color: #409eff; height: 40px; margin-top: 8px; display: flex; align-items: center; justify-content: center; padding: 0 15px; min-width: 80px; *zoom: 1; } .upload_file { font-size: 20px; opacity: 0; position: absolute; filter: alpha(opacity=0); width: 60px; } 最后 前端的日益强大导致很多功能都可以在前端去直接实现,并且可以减少服务器压力。\n当然单纯地去实现这样的数据传输,尤其对于重要数据,是很不安全的,因此在前后端数据传输的时候,可以加上加密校验,这个后期会来写的。\n参考文章 为了实现该功能参考了如下大佬的文章:\n【Vue 笔记】Vue 读取excel数据并生成数组 vue前端导入并解析excel表格操作指南 css input[type=file] 样式美化,input上传按钮美化 ","date":"2020-03-19T13:11:01Z","permalink":"https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/","title":"vue实现纯前端导入与解析excel表格文件"},{"content":"报错 使用flask_sqlalchemy,服务端出现500错误,日志显示报错如下:\n1 MySQL server has gone away 解决 添加配置:\n1 SQLALCHEMY_POOL_RECYCLE = 280 该配置作用是设置多少秒后回收连接,如果不提供值,默认为 2 小时。此处将其设置为280秒。\n该配置原文解释:\nNumber of seconds after which a connection is automatically recycled. This is required for MySQL, which removes connections after 8 hours idle by default. Note that Flask-SQLAlchemy automatically sets this to 2 hours if MySQL is used.\n","date":"2020-03-12T21:44:36Z","permalink":"https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/","title":"Flask_sqlalchemy报错MySQL server has gone away解决"},{"content":"Restful作为目前流行的api设计规范,在flask上也有较好的实践,即为flask_restful。我们在使用flask_restful的时候,当代码量达到一定程度,需要将视图函数模块化。然而在Flask之前一直使用Blueprint模块化,那么flask_restful如何模块化呢?底下就来瞅瞅!\n当前架构 1 2 3 4 5 server/ ├── __init__.py ├── commands.py ├── config.py └── views.py __init__.py:将该文件夹标示为一个模块 commands.py:额外命令,如初始化数据库 config.py:配置文件 views.py:视图函数 此为一个简单的flask项目架构,我们可以将flask中的各个功能拆分出来,分给不同文件,最后在__init__.py导入。\n1 2 3 4 5 6 7 8 9 10 11 12 from flask import Flask from flask_cors import CORS from flask_restful import Api app = Flask(\u0026#39;server\u0026#39;) app.config.from_pyfile(\u0026#39;config.py\u0026#39;) CORS(app) api = Api(app) from server import commands, views 初步模块化 现在将视图函数改为一个文件夹views,然后在该文件夹下放入拆分后的文件。这里以登录和获取用户信息接口为例,架构就变成了下面这样。\n1 2 3 4 5 6 7 8 server/ ├── __init__.py ├── commands.py ├── config.py └── views ├── __init__.py ├── login.py └── info.py 此处的__init__.py文件功能如上,是将文件夹views标示为模块,但是此处的__init__py是个空白文件,但是server文件夹中的__init__.py的引入需要改变。\n1 2 3 ... from server import commands from server.views import login, info 最终模块化 此时还不够,例如登录和获取用户信息接口是User模块下的,而获取文章标题接口是Article模块的下的,因此我们继续分。\n1 2 3 4 5 6 7 8 9 10 11 12 13 server/ ├── __init__.py ├── commands.py ├── config.py └── views ├── Aricle │ ├── __init__.py │ └── title.py ├── User │ ├── __init__.py │ ├── info.py │ └── login.py └── __init__.py server文件夹中的__init__.py的引入继续改变。\n1 2 3 4 ... from server import commands from server.views.User import login, info from srever.views.Article import title 结语 至此,关于flask_restful的视图函数模块化结束,若有后续改进会继续分享,若大家有更好的方式请务必分享一下。\n当时为了解决这个问题查了很久,但是要么是介绍blueprint的,要么就是flask_restful插件入门,简直刺激\u0026hellip;\n","date":"2020-02-23T14:55:36Z","permalink":"https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/","title":"Flask_Restful视图函数模块化"},{"content":"url转为二维码 需要的库 qrcodejs2\n安装 1 npm install qrcodejs2 --save 引入 1 import QRCode from \u0026#34;qrcodejs2\u0026#34; 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div id=\u0026#34;qrcode\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; import QRCode from \u0026#34;qrcodejs2\u0026#34;; export default { methods: { GenerateQRcode() { new QRCode(\u0026#34;qrcode\u0026#34;, { // 此处的qrcode为上面div的id text: 目标url, width: 200, height: 200, colorDark: \u0026#34;#000000\u0026#34;, colorLight: \u0026#34;#ffffff\u0026#34;, correctLevel: QRCode.CorrectLevel.H }); } }, mounted() { this.GenerateQRcode(); } } \u0026lt;/script\u0026gt; 网页保存为图片 需要的库 html2canvas\n安装 1 npm install html2canvas --save 引入 1 import html2canvas from \u0026#34;html2canvas\u0026#34; 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div id=\u0026#34;container\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; import html2canvas from \u0026#34;html2canvas\u0026#34;; import QRCode from \u0026#34;qrcodejs2\u0026#34;; export default { methods: { outputImg() { const targetDom = document.getElementById(\u0026#34;container\u0026#34;); html2canvas(targetDom).then(canvas =\u0026gt; { console.log(canvas); console.log(canvas.toDataURL()); }); } }, mounted() { this.outputImg(); } } \u0026lt;/script\u0026gt; 整合 关于小程序内置浏览器的图片下载,需要一个用来生成图片的块,还需要一个img,先将其隐藏。实现步骤就是首先生成二维码,然后再将html生成图片,最后在html2canvas回调中替换img的src,并将生成图片的块隐藏,将img显示。\n当然关于这个实现方式,我看到的技术分享文章中,还有两种不同的解决方式:\n不需要html来写生成图片的块,而是使用js直接创建; 不需要替换隐藏,将生成的图片覆盖到html生成图片的块之前; 这里我只记录一下我使用的,后期会再去研究这两种实现方式。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;!--合成的图片,默认隐藏,合成之后显示--\u0026gt; \u0026lt;div v-show=\u0026#34;imgUrl.length\u0026#34;\u0026gt; \u0026lt;img :src=\u0026#34;imgUrl\u0026#34; alt=\u0026#34;生成的图片\u0026#34; class=\u0026#34;image\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!--合成图片需要的html块,默认显示,合成之后隐藏--\u0026gt; \u0026lt;div id=\u0026#34;container\u0026#34; v-show=\u0026#34;!imgUrl.length\u0026#34;\u0026gt; \u0026lt;div id=\u0026#34;qrcode\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;p\u0026gt;长按识别二维码\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; import html2canvas from \u0026#34;html2canvas\u0026#34;; export default { data() { return { imgUrl: \u0026#34;\u0026#34; } }, methods: { outputImg() { const targetDom = document.getElementById(\u0026#34;container\u0026#34;); html2canvas(targetDom).then(canvas =\u0026gt; { // 将图片src替换为canvas生成之后转换的url this.imgUrl = canvas.toDataURL(); }); }, GenerateQRcode() { new QRCode(\u0026#34;qrcode\u0026#34;, { text: 目标url, width: 200, height: 200, colorDark: \u0026#34;#000000\u0026#34;, colorLight: \u0026#34;#ffffff\u0026#34;, correctLevel: QRCode.CorrectLevel.H }); } }, mounted() { new Promise(resolve =\u0026gt; { // 先生成二维码 this.GenerateQRcode(); resove(); }) .then(() =\u0026gt; { // 再合成图片 this.outputImg(); }) } } \u0026lt;/script\u0026gt; \u0026lt;style scoped\u0026gt; // 生成之后的图片有点放肆,可以设置宽度来适应手机屏幕 .image { width: 100%; } \u0026lt;/style\u0026gt; 由此即可实现需要的功能了。\n关于后续的优化,需要解决的图片清晰度问题、跨域图片问题等,可以参考这篇文章,这位大佬写得很详细。\n","date":"2020-02-22T22:43:24Z","permalink":"https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/","title":"vue2实现实时生成二维码和将网页合成图片并在微信内置浏览器长按保存"}] \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..716056f --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,305 @@ + + + + https://blog.hunterji.com/ + 2024-01-24T23:53:17+00:00 + + https://blog.hunterji.com/post/ + 2024-01-24T23:53:17+00:00 + + https://blog.hunterji.com/tags/ + 2024-01-24T23:53:17+00:00 + + https://blog.hunterji.com/tags/%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91/ + 2024-01-24T23:53:17+00:00 + + https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/ + 2024-01-24T23:53:17+00:00 + + https://blog.hunterji.com/tags/%E6%97%A5%E5%B8%B8%E5%88%86%E4%BA%AB/ + 2023-12-05T15:56:29+00:00 + + https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/ + 2023-12-05T15:56:29+00:00 + + https://blog.hunterji.com/p/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%96%B0%E7%9A%84%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6/ + 2023-11-29T21:10:40+00:00 + + https://blog.hunterji.com/tags/rust/ + 2023-10-26T23:51:13+00:00 + + https://blog.hunterji.com/tags/webassembly/ + 2023-10-26T23:51:13+00:00 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + 2023-10-26T23:51:13+00:00 + + https://blog.hunterji.com/tags/javascript/ + 2023-06-27T00:11:12+00:00 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + 2023-06-27T00:11:12+00:00 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + 2023-06-18T18:18:31+00:00 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + 2023-06-14T17:32:28+00:00 + + https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + 2023-03-15T22:39:35+00:00 + + https://blog.hunterji.com/tags/typescript/ + 2023-02-17T13:31:56+00:00 + + https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/ + 2023-02-17T13:31:56+00:00 + + https://blog.hunterji.com/tags/golang/ + 2023-02-09T13:15:34+00:00 + + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + 2023-02-09T13:15:34+00:00 + + https://blog.hunterji.com/tags/css/ + 2023-01-28T16:09:44+00:00 + + https://blog.hunterji.com/tags/vue/ + 2023-01-28T16:09:44+00:00 + + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + 2023-01-28T16:09:44+00:00 + + https://blog.hunterji.com/tags/python/ + 2022-06-09T22:57:54+00:00 + + https://blog.hunterji.com/p/python3-socket-tcp-example/ + 2022-06-09T22:57:54+00:00 + + https://blog.hunterji.com/p/python3-socket-udp-example/ + 2022-06-09T22:57:54+00:00 + + https://blog.hunterji.com/archives/ + 2022-03-06T00:00:00+00:00 + + https://blog.hunterji.com/page/ + 2022-03-06T00:00:00+00:00 + + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + 2022-02-16T15:07:56+00:00 + + https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/ + 2021-12-29T14:24:30+00:00 + + https://blog.hunterji.com/tags/electron/ + 2021-11-29T17:29:14+00:00 + + https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + 2021-11-29T17:29:14+00:00 + + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + 2021-11-29T17:23:46+00:00 + + https://blog.hunterji.com/tags/uniapp/ + 2021-11-25T17:07:55+00:00 + + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + 2021-11-25T17:07:55+00:00 + + https://blog.hunterji.com/tags/%E5%89%8D%E7%AB%AF/ + 2021-11-25T17:07:55+00:00 + + https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/ + 2021-11-25T16:13:10+00:00 + + https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/ + 2021-11-04T20:48:09+00:00 + + https://blog.hunterji.com/p/%E4%BD%A0%E6%98%AF%E4%B8%AA%E6%88%90%E7%86%9F%E7%9A%84%E4%BB%A3%E7%A0%81%E8%A6%81%E5%AD%A6%E4%BC%9A%E8%87%AA%E5%B7%B1%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5%E4%BA%86/ + 2021-10-27T15:28:00+00:00 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + 2021-10-25T16:07:10+00:00 + + https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/ + 2021-10-23T15:31:19+00:00 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + 2021-10-19T15:26:46+00:00 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + 2021-10-18T09:58:57+00:00 + + https://blog.hunterji.com/tags/objectstorage/ + 2021-09-29T09:22:22+00:00 + + https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/ + 2021-09-29T09:22:22+00:00 + + https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/ + 2021-09-28T09:53:18+00:00 + + https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/ + 2021-08-31T09:08:57+00:00 + + https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/ + 2021-08-30T14:29:02+00:00 + + https://blog.hunterji.com/tags/swift/ + 2021-08-30T14:29:02+00:00 + + https://blog.hunterji.com/p/swiftui-reality%E5%BC%80%E5%8F%91ar%E9%A1%B9%E7%9B%AE%E8%A7%A3%E5%86%B3%E5%85%A8%E5%B1%8F%E9%97%AE%E9%A2%98/ + 2021-08-30T14:11:58+00:00 + + https://blog.hunterji.com/p/interval%E8%AE%A1%E6%97%B6%E5%99%A8%E5%9C%A8tab%E9%A1%B5%E5%88%87%E6%8D%A2%E6%88%96%E8%80%85%E9%9A%90%E8%97%8F%E6%83%85%E5%86%B5%E4%B8%8B%E5%81%9C%E6%AD%A2%E8%BF%90%E8%A1%8C/ + 2021-08-11T16:20:05+00:00 + + https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/ + 2021-08-11T13:38:49+00:00 + + https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/ + 2021-08-11T13:13:12+00:00 + + https://blog.hunterji.com/tags/node/ + 2021-07-29T13:49:13+00:00 + + https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + 2021-07-29T13:49:13+00:00 + + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AEalert%E5%BC%B9%E5%87%BA%E4%B8%A4%E6%AC%A1%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ + 2021-07-12T16:40:57+00:00 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E6%9A%97%E9%BB%91%E6%A8%A1%E5%BC%8F/ + 2021-06-08T09:20:38+00:00 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8%E7%94%9F%E7%89%A9%E8%AF%86%E5%88%ABtouch-id--face-id/ + 2021-06-07T17:00:00+00:00 + + https://blog.hunterji.com/p/swiftui-macos%E9%A1%B9%E7%9B%AE%E6%A0%B9%E6%8D%AE%E5%B1%8F%E5%B9%95%E5%A4%A7%E5%B0%8F%E8%B0%83%E6%95%B4%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F/ + 2021-06-07T16:55:50+00:00 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%8A%9F%E8%83%BD/ + 2021-06-07T16:55:39+00:00 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AEimage%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6/ + 2021-06-07T16:43:17+00:00 + + https://blog.hunterji.com/p/swiftui%E9%A1%B9%E7%9B%AE%E5%A4%8D%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + 2021-06-07T16:29:41+00:00 + + https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/ + 2021-06-07T15:12:29+00:00 + + https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/ + 2021-06-07T11:24:22+00:00 + + https://blog.hunterji.com/p/h5%E6%A3%80%E6%B5%8B%E6%89%8B%E6%9C%BA%E6%91%87%E4%B8%80%E6%91%87/ + 2021-04-28T13:07:50+00:00 + + https://blog.hunterji.com/tags/docker/ + 2021-04-14T15:13:18+00:00 + + https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + 2021-04-14T15:13:18+00:00 + + https://blog.hunterji.com/p/grafana-loki-promtail%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + 2021-04-14T14:41:47+00:00 + + https://blog.hunterji.com/p/promise-inside-request-interceptor/ + 2021-04-09T11:06:25+00:00 + + https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/ + 2021-02-05T11:16:28+00:00 + + https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/ + 2020-12-25T12:51:12+00:00 + + https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/ + 2020-12-18T14:02:30+00:00 + + https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/ + 2020-11-27T17:41:53+00:00 + + https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + 2020-11-20T15:07:06+00:00 + + https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + 2020-11-20T14:47:04+00:00 + + https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/ + 2020-11-02T20:51:02+00:00 + + https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/ + 2020-11-02T20:24:35+00:00 + + https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/ + 2020-10-14T23:21:39+00:00 + + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + 2020-10-09T21:12:50+00:00 + + https://blog.hunterji.com/tags/nginx/ + 2020-08-15T22:07:58+00:00 + + https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/ + 2020-08-15T22:07:58+00:00 + + https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/ + 2020-08-11T18:42:42+00:00 + + https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/ + 2020-08-11T09:08:20+00:00 + + https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/ + 2020-08-10T00:00:54+00:00 + + https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/ + 2020-08-05T15:49:56+00:00 + + https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/ + 2020-08-04T09:58:55+00:00 + + https://blog.hunterji.com/p/sftp%E9%83%A8%E7%BD%B2%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E8%AE%B0%E5%BD%95/ + 2020-07-23T16:56:30+00:00 + + https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/ + 2020-06-23T13:46:21+00:00 + + https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/ + 2020-06-03T15:35:33+00:00 + + https://blog.hunterji.com/p/linux%E5%88%86%E4%BA%AB%E6%96%87%E4%BB%B6%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BA%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/ + 2020-06-02T19:05:56+00:00 + + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + 2020-05-22T15:11:05+00:00 + + https://blog.hunterji.com/p/v-charts%E6%B7%BB%E5%8A%A0%E5%9B%BE%E8%A1%A8%E6%A0%87%E9%A2%98/ + 2020-03-26T14:54:27+00:00 + + https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/ + 2020-03-21T23:48:37+00:00 + + https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/ + 2020-03-20T12:19:52+00:00 + + https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/ + 2020-03-19T13:11:01+00:00 + + https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/ + 2020-03-12T21:44:36+00:00 + + https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/ + 2020-02-23T14:55:36+00:00 + + https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/ + 2020-02-22T22:43:24+00:00 + + https://blog.hunterji.com/about/ + + https://blog.hunterji.com/categories/ + + https://blog.hunterji.com/search/ + + diff --git a/tags/css/index.html b/tags/css/index.html new file mode 100644 index 0000000..d9d38a4 --- /dev/null +++ b/tags/css/index.html @@ -0,0 +1,548 @@ + + + + +Tag: CSS - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

2 pages

+

CSS

+ +
+
+
+ +
+ + + + + +
+ + + +
+
+ + + + + diff --git a/tags/css/index.xml b/tags/css/index.xml new file mode 100644 index 0000000..4df0f08 --- /dev/null +++ b/tags/css/index.xml @@ -0,0 +1,510 @@ + + + + CSS on 开发者小橙 + https://blog.hunterji.com/tags/css/ + Recent content in CSS on 开发者小橙 + Hugo -- gohugo.io + en-us + Sat, 28 Jan 2023 16:09:44 +0000 + wow.js和animate css在vue3中的应用 + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + Sat, 28 Jan 2023 16:09:44 +0000 + + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + <h2 id="环境">环境</h2> +<ul> +<li>vue 3.2</li> +<li>typescript 4.7.4</li> +<li>wow.js 1.2.2</li> +<li>animate.css 4.1.1</li> +</ul> +<h2 id="animatecss">animate.css</h2> +<h3 id="下载">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add animate.css -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;animate.css&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<p>需要注意的是,animate css在4.0之后使用<code>animate__</code>前缀</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="动画延迟">动画延迟</h3> +<h4 id="官方方法">官方方法</h4> +<p>官方给出的动画延迟是<code>animate__delay-2s</code>、<code>animate__delay-3s</code> &hellip;&hellip;</p> +<p>直接在class中添加即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animate__delay-2s&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="自定义延迟">自定义延迟</h4> +<p>特殊场景需要使用不同于官方的延迟时间,因此可以自定义延迟时间,直接声明延迟的类,然后在class上加入即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-1</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">100</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">300</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-3</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">500</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-4</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">700</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-5</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">900</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-2&#34;</span><span class="p">&gt;</span>Another animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="wowjs">wow.js</h2> +<h3 id="下载-1">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add wow.js -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">WOW</span> <span class="nx">from</span> <span class="s1">&#39;wow.js&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">WOW</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">boxClass</span><span class="o">:</span> <span class="s1">&#39;wow&#39;</span><span class="p">,</span> <span class="c1">// 类名,在用户滚动时显示隐藏的框。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">animateClass</span><span class="o">:</span> <span class="s1">&#39;animate__animated&#39;</span><span class="p">,</span> <span class="c1">// 触发CSS动画的类名称 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">offset</span><span class="o">:</span> <span class="mi">300</span><span class="p">,</span> <span class="c1">// 定义浏览器视口底部与隐藏框顶部之间的距离。当用户滚动并到达此距离时,隐藏的框会显示出来。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mobile</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在移动设备上打开/关闭WOW.js。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">live</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在页面上同时检查新的WOW元素。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}).</span><span class="nx">init</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用-1">使用</h3> +<p>使用<code>wow</code>直接替代<code>animate__animated</code>即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;wow animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>由于写一个页面需要使用到wow,好多年没用过了,查了一下文档超多版本教程,使用起来各种不成功,难受&hellip;暂时也没找到可替代的方案&hellip;</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/graingert/wow" target="_blank" rel="noopener" + >wow</a></li> +<li><a class="link" href="https://animate.style/" target="_blank" rel="noopener" + >animate css</a></li> +</ul> + + + + tailwindcss基础 + https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/ + Fri, 05 Feb 2021 11:16:28 +0000 + + https://blog.hunterji.com/p/tailwindcss%E5%9F%BA%E7%A1%80/ + <h2 id="简介">简介</h2> +<blockquote> +<p>Rapidly build modern websites without ever leaving your HTML.</p> +</blockquote> +<p><a class="link" href="https://tailwindcss.com/" target="_blank" rel="noopener" + >Tailwind CSS</a>可以快速建立现代网站,而无需离开HTML。其特性是原子化,很像的<code>BootStrap</code>的css。</p> +<p>通俗点解释就是,其封装了很多独立的css样式,只需要在html中添加<code>class</code>即可调用,而不需要去从头写css样式。</p> +<h2 id="安装">安装</h2> +<h3 id="下载包">下载包</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install tailwindcss@latest postcss@latest autoprefixer@latest +</span></span></code></pre></td></tr></table> +</div> +</div><p>可能会遇到如下报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Error: PostCSS plugin tailwindcss requires PostCSS 8. +</span></span></code></pre></td></tr></table> +</div> +</div><p>那就需要降低<code>PostCSS</code>的版本。如下,先卸载,再去安装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm uninstall tailwindcss postcss autoprefixer +</span></span><span class="line"><span class="cl">npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加tailwind作为postcss插件">添加Tailwind作为PostCSS插件</h3> +<p>添加<code>tailwindcss</code>和<code>autoprefixer</code>到<code>PostCSS</code>配置。大部分情况下作为<code>postcss.config.js</code>文件放在项目的顶级路径下。其也能作为<code>.postcssrc</code>文件,或者使用<code>postcss</code>键放在<code>package.json</code>文件中。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// postcss.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">tailwindcss</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">autoprefixer</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建配置文件">创建配置文件</h3> +<p>如果想自定义安装,当使用<code>npm</code>安装<code>tailwindcss</code>时候需要使用tailwind命令行去生成一个配置文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npx tailwindcss init +</span></span></code></pre></td></tr></table> +</div> +</div><p>这将会创建一个最小化的<code>tailwind.config.js</code>文件,其位于项目的顶级路径下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// tailwind.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">purge</span><span class="o">:</span> <span class="p">[],</span> +</span></span><span class="line"><span class="cl"> <span class="nx">darkMode</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// or &#39;media&#39; or &#39;class&#39; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">theme</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">variants</span><span class="o">:</span> <span class="p">{},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在css中包含tailwind">在CSS中包含Tailwind</h3> +<p>创建<code>styles.css</code>文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* ./your-css-folder/styles.css */</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">base</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">components</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">utilities</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>引入该文件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./styles.css&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="构建css">构建CSS</h3> +<p>为生产而构建时,确保配置清除选项以删除任何最小文件大小的未使用类。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// tailwind.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">purge</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;./index.html&#34;</span><span class="p">,</span> <span class="s2">&#34;./src/**/*.{vue,js,ts,jsx,tsx}&#34;</span><span class="p">],</span> <span class="c1">// 修改此行 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">darkMode</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// or &#39;media&#39; or &#39;class&#39; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">theme</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">variants</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">extend</span><span class="o">:</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[]</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="简要说明">简要说明</h2> +<p>由于其样式属性巨多,此处只举几例作简要说明,讲解基础用法。在开始不熟悉的情况下,要开着其手册查询。</p> +<h3 id="width">Width</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>w-0</td> +<td>width: 0px;</td> +</tr> +<tr> +<td>w-1</td> +<td>width: 0.25rem;</td> +</tr> +<tr> +<td>w-1/2</td> +<td>width: 50%;</td> +</tr> +<tr> +<td>w-full</td> +<td>width: 100%;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;w-1/2&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="padding">Padding</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>p-0</td> +<td>padding: 0px;</td> +</tr> +<tr> +<td>p-5</td> +<td>padding: 1.25rem;</td> +</tr> +<tr> +<td>pl-1</td> +<td>padding-left: 0.25rem;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;p-5&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="position">Position</h3> +<table> +<thead> +<tr> +<th>Class</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td>static</td> +<td>position: static;</td> +</tr> +<tr> +<td>fixed</td> +<td>position: fixed;</td> +</tr> +<tr> +<td>absolute</td> +<td>position: absolute;</td> +</tr> +<tr> +<td>&hellip;</td> +<td>&hellip;</td> +</tr> +</tbody> +</table> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--示例--&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;static&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Static parent<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;absolute bottom-0 left-0 ...&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Absolute child<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="flex垂直居中">Flex垂直居中</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-row justify-center items-center&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>1<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>2<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="简单案例">简单案例</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col justify-center items-center p-20&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in 10&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-row justify-between items-center w-1/5 bg-gray-100 m-5 p-10 cursor-pointer shadow rounded hover:shadow-lg transition duration-300 ease-in-out&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;@/assets/message.png&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;logo&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;50px&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;50px&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col ml-5&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-lg&#34;</span><span class="p">&gt;</span>今天晚上加{{ item }}个鸡腿<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-sm text-gray-500&#34;</span><span class="p">&gt;</span>2020.2.{{ item }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/css/page/1/index.html b/tags/css/page/1/index.html new file mode 100644 index 0000000..31742a6 --- /dev/null +++ b/tags/css/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/css/ + + + + + + diff --git a/tags/docker/index.html b/tags/docker/index.html new file mode 100644 index 0000000..7d81472 --- /dev/null +++ b/tags/docker/index.html @@ -0,0 +1,559 @@ + + + + +Tag: Docker - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

3 pages

+

Docker

+ +
+
+
+ +
+ + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/docker/index.xml b/tags/docker/index.xml new file mode 100644 index 0000000..4dda217 --- /dev/null +++ b/tags/docker/index.xml @@ -0,0 +1,353 @@ + + + + Docker on 开发者小橙 + https://blog.hunterji.com/tags/docker/ + Recent content in Docker on 开发者小橙 + Hugo -- gohugo.io + en-us + Wed, 14 Apr 2021 15:13:18 +0000 + Grafana+Loki+Docker Driver Client日志收集方案 + https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + Wed, 14 Apr 2021 15:13:18 +0000 + + https://blog.hunterji.com/p/grafana-loki-docker-driver-client%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E6%96%B9%E6%A1%88/ + <h2 id="简介">简介</h2> +<p>具体日志采集方案在<code>Grafana+Loki+Promtail日志收集方案</code>文章中已经介绍过,此处不再重复介绍。不太了解的小伙伴儿赶紧去复习!</p> +<p>此处主要是记录下<code>Docker Driver Client</code>方式的部署。</p> +<h2 id="docker-plugin">docker plugin</h2> +<h3 id="安装">安装</h3> +<p>安装<code>loki</code>插件。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证">验证</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ docker plugin ls +</span></span><span class="line"><span class="cl">ID NAME DESCRIPTION ENABLED +</span></span><span class="line"><span class="cl">ac720b8fcfdb loki Loki Logging Driver <span class="nb">true</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="开启禁用">开启/禁用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin <span class="nb">enable</span> loki +</span></span><span class="line"><span class="cl">docker plugin disable loki --force +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="卸载">卸载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker plugin rm loki +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="部署">部署</h2> +<h3 id="docker-composeyml">docker-compose.yml</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">loki</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/loki:2.0.0</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3100:3100&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>-<span class="l">config.file=/etc/loki/local-config.yaml</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">grafana</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:latest</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3000:3000&#34;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">networks</span><span class="p">:</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">loki</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行">运行</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker-compose up -d +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="容器运行形式">容器运行形式</h3> +<p>容器运行时需要修改<code>log-driver</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> --log-driver<span class="o">=</span>loki +</span></span><span class="line"><span class="cl">--log-opt loki-url<span class="o">=</span><span class="s2">&#34;http://&lt;loki-url&gt;/loki/api/v1/push&#34;</span> +</span></span><span class="line"><span class="cl">--log-opt loki-retries<span class="o">=</span><span class="m">5</span> +</span></span><span class="line"><span class="cl">--log-opt loki-batch-size<span class="o">=</span><span class="m">400</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>举个例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run --log-driver<span class="o">=</span>loki <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-url<span class="o">=</span><span class="s2">&#34;http://192.168.10.10:3100/loki/api/v1/push&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-retries<span class="o">=</span><span class="m">5</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> --log-opt loki-batch-size<span class="o">=</span><span class="m">400</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> -p 3000:3000 <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> nginx +</span></span></code></pre></td></tr></table> +</div> +</div><p>由此查看日志,其<code>labels</code>只有<code>container_name</code>。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://grafana.com/docs/loki/latest/clients/docker-driver/" target="_blank" rel="noopener" + >官方文档</a></li> +</ul> + + + + Docker修改时区 + https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/ + Wed, 14 Oct 2020 23:21:39 +0000 + + https://blog.hunterji.com/p/docker%E4%BF%AE%E6%94%B9%E6%97%B6%E5%8C%BA/ + <h2 id="前言">前言</h2> +<p>在使用<code>Docker</code>时,其默认时区并非使用者所在时区,需要进行修改。对于单个容器,当前修改有几种常见方式,比如直接映射宿主机时区到容器内,而本文介绍的为使用<code>Dockerfile</code>来直接修改镜像时区。此处仅以常见几个基础容器为例来介绍。</p> +<h2 id="常见容器">常见容器</h2> +<h3 id="alpine"><strong>Alpine</strong></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> alpine:latest</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装tzdata</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apk add --no-cache tzdata<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置时区</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">TZ</span><span class="o">=</span><span class="s2">&#34;Asia/Shanghai&#34;</span><span class="err"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t alpine:time . +</span></span><span class="line"><span class="cl">docker run --rm -it alpine:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="ubuntu"><strong>Ubuntu</strong></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> ubuntu</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置localtime</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 此处需要优先设置localtime,否则安装tzdata将会进入时区选择</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装tzdata</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apt-get update <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> <span class="o">&amp;&amp;</span> apt-get install tzdata -y <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="se"></span> <span class="o">&amp;&amp;</span> apt-get clean<span class="err"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t ubuntu:time . +</span></span><span class="line"><span class="cl">docker run --rm -it ubuntu:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="debian">Debian</h3> +<ul> +<li>Debian中已经安装了<code>tzdata</code>,所以跟<code>Ubuntu</code>有所不通过</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> debian</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 修改设置dpkg为自动配置</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>noninteractive +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">RUN</span> ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> dpkg-reconfigure -f noninteractive tzdata<span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 修改设置dpkg为手动输入选择操作</span><span class="err"> +</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>dialog +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>验证</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t debian:time . +</span></span><span class="line"><span class="cl">docker run --rm -it debian:time date +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>此处不再列举太多,主要解决方式为安装<code>tzdata</code>,然后修改时区。</p> + + + + docker中php上传大小限制 + https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/ + Wed, 03 Jun 2020 15:35:33 +0000 + + https://blog.hunterji.com/p/docker%E4%B8%ADphp%E4%B8%8A%E4%BC%A0%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6/ + <h2 id="问题">问题</h2> +<p>使用<code>docker</code>运行的<code>wordpress</code>,有报错<code>The uploaded file exceeds the upload_max_filesize directive in php.ini</code>。</p> +<h2 id="过程">过程</h2> +<p>按照一般的方式去修改文件<code>php.ini</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">upload_max_filesize</span> <span class="o">=</span> 30M +</span></span><span class="line"><span class="cl"><span class="nv">post_max_size</span> <span class="o">=</span> 30M +</span></span></code></pre></td></tr></table> +</div> +</div><p>容器中有两个<code>php.ini</code>文件:</p> +<ul> +<li>php.ini-development</li> +<li>php.ini-production</li> +</ul> +<p>都修改之后却并不生效。</p> +<h2 id="解决">解决</h2> +<p>在文件夹<code>conf.d</code>中添加文件<code>uploads.ini</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">file_uploads = On +</span></span><span class="line"><span class="cl">memory_limit = 64M +</span></span><span class="line"><span class="cl">upload_max_filesize = 64M +</span></span><span class="line"><span class="cl">post_max_size = 64M +</span></span><span class="line"><span class="cl">max_execution_time = 600 +</span></span></code></pre></td></tr></table> +</div> +</div><p>文件夹<code>conf.d</code>位置需要看具体环境:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php -i <span class="p">|</span> grep php.ini +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后在其目录下找到文件夹<code>conf.d</code>。</p> +<p>也可以通过在运行容器时候直接将该文件映射进入。</p> + + + + + diff --git a/tags/docker/page/1/index.html b/tags/docker/page/1/index.html new file mode 100644 index 0000000..c944c46 --- /dev/null +++ b/tags/docker/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/docker/ + + + + + + diff --git a/tags/electron/index.html b/tags/electron/index.html new file mode 100644 index 0000000..2cd42f0 --- /dev/null +++ b/tags/electron/index.html @@ -0,0 +1,581 @@ + + + + +Tag: Electron - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

5 pages

+

Electron

+ +
+
+
+ +
+ + + + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/electron/index.xml b/tags/electron/index.xml new file mode 100644 index 0000000..1e828fe --- /dev/null +++ b/tags/electron/index.xml @@ -0,0 +1,1140 @@ + + + + Electron on 开发者小橙 + https://blog.hunterji.com/tags/electron/ + Recent content in Electron on 开发者小橙 + Hugo -- gohugo.io + en-us + Mon, 29 Nov 2021 17:29:14 +0000 + electron的__dirname not defined报错解决 + https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:29:14 +0000 + + https://blog.hunterji.com/p/electron%E7%9A%84__dirname-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue cli plugin electron builder开发项目,用到涉及node相关的功能,比如<code>fs</code>、<code>path</code>,出现报错<code>__dirname not defined </code>。</p> +<h2 id="解决">解决</h2> +<p>该问题在于需要开启electron对于node操作的支持。</p> +<h3 id="安装typesnode">安装@types/node</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add @types/node -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="修改配置">修改配置</h3> +<p>修改electron的配置文件,此处我的配置文件为<code>src/background.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegrationInWorker</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue3+ts+electron不支持require is not defined报错解决 + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:23:46 +0000 + + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue3+typescript+electron开发时,遇到一个报错为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Uncaught ReferenceError: require is not defined +</span></span></code></pre></td></tr></table> +</div> +</div><p>点进去是<code>module.exports = require(&quot;events&quot;)</code>,并不是自己的代码中的<code>require</code>,因此无法改变写法只能让项目去支持它。</p> +<h2 id="解决">解决</h2> +<p>在electron的配置文件中,新增或者修改如下配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">contextIsolation</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/64706829/electron-tedious-requireevents-is-not-defined" target="_blank" rel="noopener" + >Electron/Tedious: require(&ldquo;events&rdquo;) is not defined</a></li> +</ul> + + + + Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + Mon, 25 Oct 2021 16:07:10 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + <h2 id="简介">简介</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a>中,我们了解了如何使用<code>Vite</code>和<code>Electron</code>来快速构建一个Vue3桌面应用。但是,之前构建的应用仅仅是一个简单的版本。在开发过程中,为了更好的开发体验,在开发electron的时候,肯定也希望能有动态模块热重载(HMR),更别说vite那迅雷不及掩耳盗铃儿响叮当之势的加载速度。</p> +<p>因此,接着上一篇文章所完成的项目代码,我们来完成<code>Vite</code>和<code>Electron</code>开发时的动态模块热重载功能。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,可利用electron中的<code>mainWindow.loadURL(&lt;your-url&gt;)</code>来实现。</p> +<p>对于动态模块热重载功能来说,无论是webpack还是vite,其都是将构建内容存入内存,因此我们无法使用<code>mainWindow.loadFile('dist/index.html')</code>这样加载文件的方式。</p> +<p>但是,单纯地改变该配置也是不行的,需要使用vite将开发服务器运行起来,可以正常运行动态模块热重载,而electron直接加载其开发服务器可访问的url,即<code>http://localhost:3000</code>。</p> +<h2 id="实现步骤">实现步骤</h2> +<h3 id="编辑mainjs">编辑main.js</h3> +<p>将<code>mainWindow.loadFile('dist/index.html')</code>更新为<code>mainWindow.loadURL(&quot;http://localhost:3000&quot;)</code>,更新后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span><span class="s2">&#34;http://localhost:3000&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑viteconfigjs">编辑vite.config.js</h3> +<p>修改文件<code>vite.config.js</code>的<code>base</code>,修改后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// vite.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="s2">&#34;./&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="同时开启vite和electron服务">同时开启vite和electron服务</h3> +<p>为了使vite和electron正常运行,需要先运行vite,使得其开发服务器的url可以正常访问,然后再开启electron去加载url。</p> +<p>此处需要安装两个库:</p> +<ul> +<li><strong>concurrently</strong>:阻塞运行多个命令,<code>-k</code>参数用来清除其它已经存在或者挂掉的进程</li> +<li><strong>wait-on</strong>:等待资源,此处用来等待url可访问</li> +</ul> +<p>首先来安装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D concurrently wait-on +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着更新文件<code>package.json</code>,<code>scripts</code>新增两条命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"> <span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>现已添加两条命令:</p> +<ul> +<li><code>yarn electron</code>为等待tcp协议3000端口可访问,然后执行electron</li> +<li><code>yarn electron:serve</code>为阻塞执行开发服务器运行和<code>yarn electron</code>命令</li> +</ul> +<p>运行项目只要执行命令<code>yarn electron:serve</code>即可,当修改项目文件时,桌面应用也将自动更新。</p> +<h2 id="参考文件">参考文件</h2> +<ul> +<li><a class="link" href="https://cn.vitejs.dev/guide/why.html#slow-server-start" target="_blank" rel="noopener" + >为什么选vite</a></li> +<li><a class="link" href="https://dev.to/brojenuel/vite-vue-3-electron-5h4o" target="_blank" rel="noopener" + >vite+vue3+electron+typescript</a></li> +</ul> + + + + Vite+Electron快速构建一个VUE3桌面应用(三)——打包 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + Tue, 19 Oct 2021 15:26:46 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + <h2 id="简介">简介</h2> +<p>上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a>完成了开发时的动态模块热重载功能,现在是时候来看看怎么完成最后一步——打包了。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,重点还是在于<code>mainWindow.loadURL()</code>。</p> +<p>打包后还是加载<code>http://localhost:3000</code>是无法运行的,因此,此处需要先用vite打包好,然后使用<code>electron-builder</code>加载vite打包后的文件进行打包。</p> +<p>为了代码能够根据不同环境在运行时加载<code>http://localhost:3000</code>,在打包时加载文件,此处需要使用环境变量来切换生产和开发环境。</p> +<h2 id="实现">实现</h2> +<h3 id="环境变量">环境变量</h3> +<p>此处使用环境变量<code>NODE_ENV</code>来切换生产和开发环境,生产环境为<code>NODE_ENV=production</code>,开发环境为<code>NODE_ENV=development</code>,若有其它如<code>release</code>等环境可在此基础上拓展。</p> +<h3 id="创建electron文件夹">创建electron文件夹</h3> +<p>在项目根目录下创建文件夹<code>electron</code>,将<code>main.js</code>和<code>preload.js</code>文件移动进来。其结构如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── electron +</span></span><span class="line"><span class="cl">│   ├── main.js +</span></span><span class="line"><span class="cl">│   └── preload.js +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>若还是不太明白可以看看<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >源码</a>中文件结构。</p> +<h3 id="编辑electronmainjs">编辑electron/main.js</h3> +<p>该文件主要是需要根据环境变量切换electron加载的内容,修改内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>修改后的完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// electron/main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">NODE_ENV</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s2">&#34;development&#34;</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">webContents</span><span class="p">.</span><span class="nx">openDevTools</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑packagejson">编辑package.json</h3> +<p>首先修改<code>main </code>属性,将<code>main: main.js</code>改为<code>main: electron/main.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="err">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,编辑<code>build</code>属性:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;build&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.your-website.your-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;ElectronApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 &lt;your-name&gt;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,更新<code>scripts</code>属性。</p> +<p>此处需要先安装两个库:</p> +<ul> +<li><strong><code>cross-env</code></strong>: 该库让开发者只需要注重环境变量的设置,而无需担心平台设置</li> +<li><strong><code>electron-builder</code></strong>: electron打包库</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D cross-env electron-builder +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后的<code>scripts</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> <span class="c1">// 此处需要设置环境变量以保证开发时加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> <span class="c1">// 新增打包命令 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,更新后的<code>package.json</code>完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron-builder&#34;</span><span class="p">:</span> <span class="s2">&#34;^22.13.1&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.my-website.my-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;MyApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="打包">打包</h2> +<p>直接执行打包命令即可开始打包。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:build +</span></span></code></pre></td></tr></table> +</div> +</div><p>打包完成之后,会多出两个文件夹<code>dist</code>和<code>dist_electron</code>,其文件结构如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── dist +</span></span><span class="line"><span class="cl">│   ├── assets +</span></span><span class="line"><span class="cl">│   ├── favicon.ico +</span></span><span class="line"><span class="cl">│   └── index.html +</span></span><span class="line"><span class="cl">├── dist_electron +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip.blockmap +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg.blockmap +</span></span><span class="line"><span class="cl">│   ├── builder-debug.yml +</span></span><span class="line"><span class="cl">│   ├── builder-effective-config.yaml +</span></span><span class="line"><span class="cl">│   └── mac +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>至此,便完成了打包。</p> +<p>后面再来写写关于electron的优化,减少electron打包后应用的体积。(这玩意儿确实打包下来有点大呢/狗头)</p> + + + + Vite+Electron快速构建一个VUE3桌面应用 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + Mon, 18 Oct 2021 09:58:57 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + <h2 id="简介">简介</h2> +<p>首先,介绍下<code>vite</code>和<code>Electron</code>。</p> +<ul> +<li>Vite是一种新型前端构建工具,能够显著提升前端开发体验。由尤大推出,其发动态表示“再也回不去webpack了&hellip;”</li> +<li>Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。</li> +</ul> +<p>当开始想用vue去开发一个桌面应用时,首先去搜索下,了解到当前如下两种现成方案:</p> +<ul> +<li><strong>electron-vue</strong>: 该项目集成度较好,封装较为完整,中文搜索下来文章较多也是该方案,可以直接上手去使用。但是,问题在于其内置electron的版本太低,写文章时看到的版本是2.0.4,而最新的electron版本是15.1.2。</li> +<li><strong>Vue CLI Plugin Electron Builder</strong>: 该方案是集成到到<code>vue-cli</code>中使用,使用<code>vue add electron-builder</code>后可直接上手,免去了基础配置的步骤。但是其只能在<code>vue-cli</code>下使用,无法配合<code>vite</code>来使用。</li> +</ul> +<p>因此,若要使用<code>vite</code>和<code>electron</code>,还需要自己来配置。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1</a></p> +<h2 id="创建一个vite项目">创建一个Vite项目</h2> +<h3 id="安装-vite">安装 vite</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建项目">创建项目</h3> +<p>创建命令如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite &lt;your-vue-app-name&gt; --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处创建一个项目,名为kuari。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite kuari --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="进入且运行">进入且运行</h3> +<p>进入项目,在运行前需要先安装下依赖。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> kuari +</span></span><span class="line"><span class="cl">yarn install +</span></span><span class="line"><span class="cl">yarn dev +</span></span></code></pre></td></tr></table> +</div> +</div><p>在运行命令敲下的一瞬间,几乎是已经在运行了,不愧是vite。此时按照输出,打开地址预览,即可看到初始化页面。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gveqwmm4eij61bc0u03zy02.jpg" alt="截屏2021-10-14 下午12.50.48" style="zoom:50%;" /> +<p>至此一个基础的vite项目创建完成。</p> +<h2 id="配置electron">配置Electron</h2> +<h3 id="官方文档">官方文档</h3> +<p>在<a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网的快速入门文档</a>中,有官方给出的利用html、javascript、css来创建一个electron应用的案例,vite+electron的方案也借鉴其中。</p> +<h3 id="安装">安装</h3> +<p>首先安装electron至vite应用。目前electron的版本为<code>^15.1.2,</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev electron +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置文件">配置文件</h3> +<p>####config.js</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">path</span> <span class="nx">from</span> <span class="s1">&#39;path&#39;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;./dist/&#39;</span><span class="p">),</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>main.js</code>,需要注意的是,该内容中<code>index.html</code>的加载路径跟electron官网给的配置不同。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadFile</span><span class="p">(</span><span class="s1">&#39;dist/index.html&#39;</span><span class="p">)</span> <span class="c1">// 此处跟electron官网路径不同,需要注意 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>preload.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// preload.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 所有Node.js API都可以在预加载过程中使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 它拥有与Chrome扩展一样的沙盒。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;DOMContentLoaded&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">replaceText</span> <span class="o">=</span> <span class="p">(</span><span class="nx">selector</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="nx">element</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="nx">text</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">dependency</span> <span class="k">of</span> <span class="p">[</span><span class="s1">&#39;chrome&#39;</span><span class="p">,</span> <span class="s1">&#39;node&#39;</span><span class="p">,</span> <span class="s1">&#39;electron&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">replaceText</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">dependency</span><span class="si">}</span><span class="sb">-version`</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">versions</span><span class="p">[</span><span class="nx">dependency</span><span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####json</p> +<p>为了确保能够运行相关electron的命令,需要修改<code>package.json</code>文件。</p> +<p>首先需要去设置<code>main</code>属性,electron默认会去在开始时寻找项目根目录下的<code>index.js</code>文件,此处我们使用的是<code>main.js</code>,所以需要去定义下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后我们需要新增electron的运行命令。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;electron .&#34;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>直接在终端输入如下命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:serve +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着我们就可以看到我们桌面应用就出来咯!</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gves3xrayzj612f0u0myy02.jpg" alt="截屏2021-10-14 下午1.32.38" style="zoom:50%;" /> +<h2 id="最后">最后</h2> +<p>之前做项目一直用的Vue CLI Plugin Electron Builder,这次有个项目先用electron开发一下,推一波看看,后期看情况swift重新开发一个mac的桌面应用。也刚好尝尝鲜,一直没有机会试试vite。</p> +<p>electron这个东东确实很方便,就是打包出来的应用体积太大,真的是硬伤啊。这次目标人群首先是windows用户,所以上electron吧!</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网快速入门</a></li> +<li><a class="link" href="https://vitejs.dev/" target="_blank" rel="noopener" + >Vite官网</a></li> +<li><a class="link" href="https://learnvue.co/2021/05/build-vue-3-desktop-apps-in-just-5-minutes-vite-electron-quick-start-guide/" target="_blank" rel="noopener" + >Build vue3 desktop apps in just 5 minutes</a></li> +</ul> + + + + + diff --git a/tags/electron/page/1/index.html b/tags/electron/page/1/index.html new file mode 100644 index 0000000..cff74ee --- /dev/null +++ b/tags/electron/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/electron/ + + + + + + diff --git a/tags/golang/index.html b/tags/golang/index.html new file mode 100644 index 0000000..e2b9155 --- /dev/null +++ b/tags/golang/index.html @@ -0,0 +1,682 @@ + + + + +Tag: Golang - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

13 pages

+

Golang

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/tags/golang/index.xml b/tags/golang/index.xml new file mode 100644 index 0000000..4f7dce5 --- /dev/null +++ b/tags/golang/index.xml @@ -0,0 +1,3572 @@ + + + + Golang on 开发者小橙 + https://blog.hunterji.com/tags/golang/ + Recent content in Golang on 开发者小橙 + Hugo -- gohugo.io + en-us + Thu, 09 Feb 2023 13:15:34 +0000 + WebAssembly -- 未来前端开发的必备技能 + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + Thu, 09 Feb 2023 13:15:34 +0000 + + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + <img src="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/cover.jpg" alt="Featured image of post WebAssembly -- 未来前端开发的必备技能" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<h2 id="快速上手">快速上手</h2> +<h3 id="用go写一个hello-world">用go写一个hello world</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello, WebAssembly!&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="将go文件编译成wasm文件">将go文件编译成wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">GOOS</span><span class="o">=</span>js <span class="nv">GOARCH</span><span class="o">=</span>wasm go build -o static/main.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="拷贝出wasm_execjs">拷贝出wasm_exec.js</h3> +<p>该文件为go的wasm的js支持文件</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cp <span class="s2">&#34;</span><span class="k">$(</span>go env GOROOT<span class="k">)</span><span class="s2">/misc/wasm/wasm_exec.js&#34;</span> static +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="html文件调用wasm文件">html文件调用wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证调用">验证调用</h3> +<p>浏览器加载html文件,f12打开控制台,可以看到wasm的打印消息。</p> +<h2 id="go与js的类型转换">go与js的类型转换</h2> +<h3 id="类型映射">类型映射</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><p>如上为官方给出的go与js的类型映射表。</p> +<p>比如在go中调用js函数,参数为<code>array</code>,那么就可以直接将go的<code>[]interface{}</code>类型的变量作为参数使用。</p> +<h3 id="函数转换数组">函数转换数组</h3> +<p><code>syscall/js</code>提供了两个函数:</p> +<ul> +<li>CopyBytesToGo:<code>func CopyBytesToGo(dst []byte, src Value) int</code></li> +<li>CopyBytesToJS:<code>func CopyBytesToJS(dst Value, src []byte) int</code></li> +</ul> +<p>两者对于go而言,类型都是<code>[]byte</code>,但是对于js而言,需要<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型,否则就会报错。</p> +<p>那么,如何在go中生成一个<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型的变量呢?官方的类型映射表也没有啊&hellip;那么就看下一步。</p> +<h3 id="其余类型">其余类型</h3> +<p>对于非官方类型映射表内的类型,和官方提供的两个数据类型转换之外的类型,可以通过一种通用的方式来生成,以上一步的<code>Uint8Array</code>为例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">length</span><span class="o">&gt;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>实际使用案例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// goData []byte{...} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">jsData</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="nx">len</span><span class="p">(</span><span class="nx">goData</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">CopyBytesToJS</span><span class="p">(</span><span class="nx">jsData</span><span class="p">,</span> <span class="nx">goData</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>那么,比如js中的<code>Date</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">dateConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Date&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">dateConstructor</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s2">&#34;2020-10-01&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="极端情况">极端情况</h3> +<p>好吧,还有最后一个方案,如果遇到极端情况,上述方案都无法解决,那么请转换成字符串吧!让go和js用各自的方法分别处理一波,得到自己想要的结果或者给出各自想给的数据。</p> +<h2 id="js调用go函数">js调用go函数</h2> +<blockquote> +<p>此处需要在go中引入<a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a>,以实现js相关的操作。</p> +</blockquote> +<h3 id="注册go函数">注册go函数</h3> +<p>将go的函数注册为js的函数,由js来进行调用。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleCount</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">count</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Int</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">js</span><span class="p">.</span><span class="nf">ValueOf</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleEvent&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleEvent</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>js.Func()</code> 接受一个函数类型作为其参数,该函数的定义是固定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"><span class="c1">// this 即 JavaScript 中的 this +</span></span></span><span class="line"><span class="cl"><span class="c1">// args 是在 JavaScript 中调用该函数的参数列表。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 返回值需用 js.ValueOf 映射成 JavaScript 的值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>js.ValueOf返回作为js的值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用">js调用</h3> +<p>在js中使用也非常简单,引入wasm文件之后,直接调用函数即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleEvent</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">// 传入参数1 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go调用js函数">go调用js函数</h2> +<p>如果在js中本身已经定义了函数,那么在go中也可以直接调用该函数,进行运算,将得出的结果在go中继续使用。</p> +<h3 id="定义js函数">定义js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="go中调用js函数">go中调用js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;add&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="c1">// 此处输出类型为js.Value,无法直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nf">Int</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// 使用.Int()将其转换为go中的类型,即可直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入wasm">引入wasm</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="结果">结果</h3> +<p>在前端调试台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;number: 3&gt; +</span></span><span class="line"><span class="cl">4 +</span></span></code></pre></td></tr></table> +</div> +</div><p>第一个结果就是js.Value的值,第二个结果则是转换成go的值,并按照逻辑进行了<code>+1</code>处理。</p> +<h2 id="回调函数解决go函数阻塞问题">回调函数/解决go函数阻塞问题</h2> +<blockquote> +<p>The Go function fn is called with the value of JavaScript&rsquo;s &ldquo;this&rdquo; keyword and the arguments of the invocation. The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.</p> +<p>Invoking the wrapped Go function from JavaScript will pause the event loop and spawn a new goroutine. Other wrapped functions which are triggered during a call from Go to JavaScript get executed on the same goroutine.</p> +<p>As a consequence, if one wrapped function blocks, JavaScript&rsquo;s event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.</p> +</blockquote> +<p><code>syscall/js</code>官方文档表明,如果go包装函数阻塞,那么js的事件循环也将被阻塞,直到函数返回,调用任何需要事件循环(如fetch)的异步js api都导致立即死锁。因此,一个阻塞函数应该显式地启动一个新的协程。</p> +<p>此处,可以在go中注册一个回调函数,加上协程实现异步,不会产生堵塞。</p> +<h3 id="注册函数">注册函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">username</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">String</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;hello, %s !&#34;</span><span class="p">,</span> <span class="nx">username</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-1">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleRender</span><span class="p">(</span><span class="s2">&#34;tom&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">hello, tom ! // 隔了3秒之后,输出了回调函数的值 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go实现promise">Go实现Promise</h2> +<p>上一步的回调函数,解决了函数阻塞问题,此处,结合回调函数实现promise,来丰富异步场景。</p> +<p>在js中,promise是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>async</code>和<code>await</code>调用,拿到结果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">printMessage</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">message</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在go中又如何构建promise呢?这里可以用到上述go与js的类型转换,创建一个promise:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="注册函数-1">注册函数</h3> +<p>go的完整实现如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">handler</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="s">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">promiseConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">promiseConstructor</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-2">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">HandleRender</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出-1">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">message: hello, world ! // 隔了3秒之后输出 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="操作dom">操作DOM</h2> +<h3 id="使用document">使用document</h3> +<p>定义一个全局的document</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">var docuemnt = js.Global().Get(&#34;document&#34;) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="获取元素">获取元素</h3> +<p>获取一个<code>id</code>为<code>container</code>的<code>div</code>,设置<code>background-color: red</code>、<code>widht: 600</code>、<code>height: 400</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;container&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElementStyle</span> <span class="p">=</span> <span class="nx">container</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;style&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;background&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="s">&#34;600px&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="s">&#34;400px&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建元素">创建元素</h3> +<p>创建一个<code>id</code>为<code>image</code>的<code>image</code>,设置<code>width:300</code>、<code>height:200</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">imageElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;createElement&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加子元素">添加子元素</h3> +<p>将<code>image</code>添加为<code>id</code>为<code>container</code>的<code>div</code>的子元素</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">containerElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;appendChild&#34;</span><span class="p">,</span> <span class="nx">imageElement</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加事件">添加事件</h3> +<p>给<code>image</code>添加右击事件,右击<code>image</code>则阻止右键菜单</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 定义响应函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">handlePreventEventCallBack</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;preventDefault&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 给image添加事件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;contextmenu&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handlePreventEventCallBack</span><span class="p">))</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里需要注意的是,当不再调用响应事件函数时,必须调用Func.Release以释放资源:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">cb</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Func</span> +</span></span><span class="line"><span class="cl"><span class="nx">cb</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="nx">any</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;button clicked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">cb</span><span class="p">.</span><span class="nf">Release</span><span class="p">()</span> <span class="c1">// 如果不再单击该按钮,则释放该函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;myButton&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;click&#34;</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="canvas">Canvas</h2> +<p>这里放一波canvas的案例,包含了一些常用方法,可以参考完成更多操作。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;math&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">radius</span> <span class="p">=</span> <span class="mi">200</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">canvas</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="nx">height</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getContext&#34;</span><span class="p">,</span> <span class="s">&#34;2d&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;beginPath&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;arc&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="nx">math</span><span class="p">.</span><span class="nx">Pi</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;lightpink&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fill&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;lineWidth&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;strokeStyle&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;stroke&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;font&#34;</span><span class="p">,</span> <span class="s">&#34;20px Comic Sans MS&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;blue&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fillText&#34;</span><span class="p">,</span> <span class="s">&#34;Hello, World !&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="o">-</span><span class="mi">60</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>渲染结果:</p> +<p><img src="https://user-images.githubusercontent.com/25321169/217725808-5dd94da2-bb43-4129-95a9-27f3f85daba5.png" + + + + loading="lazy" + + alt="go_wasm_canvas" + + +></p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a></li> +<li><a class="link" href="https://github.com/golang/go/wiki/WebAssembly" target="_blank" rel="noopener" + >go wiki WebAssembly</a></li> +<li><a class="link" href="https://geektutu.com/post/quick-go-wasm.html" target="_blank" rel="noopener" + >Go WebAssembly (Wasm) 简明教程</a></li> +<li><a class="link" href="https://withblue.ink/2020/10/03/go-webassembly-http-requests-and-promises.html" target="_blank" rel="noopener" + >Go, WebAssembly, HTTP requests and Promises</a></li> +</ul> + + + + Golang如何使用validator校验参数 + https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/ + Sat, 23 Oct 2021 15:31:19 +0000 + + https://blog.hunterji.com/p/golang%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8validator%E6%A0%A1%E9%AA%8C%E5%8F%82%E6%95%B0/ + <h2 id="简介">简介</h2> +<p>关于测试工程师,有一个笑话,是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯咖啡 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了0.7杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了-1杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了2^32杯啤酒 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯洗脚水 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯蜥蜴 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一份asdfQwer@24dg!&amp;*(@ +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,什么也没要 +</span></span><span class="line"><span class="cl">一个测试工程师走进家酒吧,又走出去又从窗户进来又从后门出去从下水道钻进来 +</span></span><span class="line"><span class="cl">一个测试工程师走进家酒吧,又走出去又进来又出去又进来又出去,最后在外面把老板打了一顿 +</span></span><span class="line"><span class="cl">一个测试工程师走进一 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯烫烫烫的锟斤拷 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了NaN杯Null +</span></span><span class="line"><span class="cl">1T测试工程师冲进一家酒吧,要了500T啤酒咖啡洗脚水野猫狼牙棒奶茶 +</span></span><span class="line"><span class="cl">1T测试工程师把酒吧拆了 +</span></span><span class="line"><span class="cl">一个测试工程师化装成老板走进一家酒吧,要了500杯啤酒,并且不付钱 +</span></span><span class="line"><span class="cl">一万个测试工程师在酒吧外呼啸而过 +</span></span><span class="line"><span class="cl">一个测试工程师走进一家酒吧,要了一杯啤酒‘;DROPTABLE酒吧 +</span></span><span class="line"><span class="cl">测试工程师们满意地离开了酒吧 +</span></span></code></pre></td></tr></table> +</div> +</div><p>这个笑话估计也只有开发才明白其中的笑点与心酸吧。</p> +<p>对于一些刚入门的开发来说,这简直就是噩梦。当初刚入门的时候我的代码也是很多毛病,经不起这样的测试,后来渐渐地经验多了后,代码的健壮性逐渐提升,也明白其中比较重要的就是参数的校验。</p> +<p>参数的使用有通过协议的接口调用(如http、rpc&hellip;&hellip;)、函数调用、库调用等等方式。</p> +<p>其实对于http api的请求来说,现在很多web框架都已经自带了参数校验的功能,基本用起来都挺爽的,也无需多讲。</p> +<p>而对于函数调用这样的常见方式,很多是要靠开发自己去校验参数的。如果仅仅是靠注释,在团队开发过程中,难免会有问题产生。起码我觉得,永远不要相信传过来的参数!</p> +<p>OK,那么来讲讲这次的题目,就是Golang中的参数校验库——validator。</p> +<p>用过Gin的小伙伴儿应该知道其<code>binding</code>参数验证器非常好用,其就是调用了validator。此处呢我们来介绍下validator的基础用法,和在一般场景下的应用案例。</p> +<h2 id="基础用法">基础用法</h2> +<h3 id="介绍">介绍</h3> +<p><code>validator</code>包源码在<a class="link" href="https://github.com/go-playground/validator" target="_blank" rel="noopener" + >github.com/go-playground/validator</a>。其基于标记实现结构和单个字段的值验证,包含如下关键功能:</p> +<ul> +<li>使用验证标记或自定义验证程序进行跨字段和跨结构验证</li> +<li>Slice、Array和Map都可以允许验证多维字段的任何或者所有级别</li> +<li>能够深入查看映射键和值以进行验证</li> +<li>通过在验证之前确定类型接口的基础类型来处理类型接口</li> +<li>处理自定义字段类型</li> +<li>允许将多个验证映射到单个标记,以便在结构上更轻松地定义验证</li> +<li>提取自定义定义的字段名,例如,可以指定在验证时提取JSON名称,并使其在结果FieldError中可用</li> +<li>可定制的i18n错误消息</li> +<li>gin web框架的默认验证器</li> +</ul> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go get github.com/go-playground/validator/v10 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="导入">导入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;github.com/go-playground/validator/v10&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证规则">验证规则</h3> +<p>此处从官方列举的各个类别中挑选部分举例说明。</p> +<h4 id="1比较">1)比较</h4> +<ul> +<li><strong>eq</strong>:相等</li> +<li><strong>gt</strong>:大于</li> +<li><strong>gte</strong>:大于等于</li> +<li><strong>lt</strong>:小于</li> +<li><strong>lte</strong>:小于等于</li> +<li><strong>ne</strong>:不等于</li> +</ul> +<h4 id="2字段">2)字段</h4> +<p>此处的字段大部分可以理解为上面的比较的tag跟<code>field</code>拼接而成,而中间有<code>cs</code>的tag为跨struct比较。</p> +<ul> +<li><strong>eqfield</strong>(=Field):必须等于Field的值</li> +<li><strong>nefield</strong>(=Field):必须不等于Field的值</li> +<li><strong>gtfield</strong>(=Field):必须大于Field的值</li> +<li><strong>eqcsfield</strong>(=Other.Field):必须等于struct Other中的Field的值</li> +</ul> +<h4 id="3网络">3)网络</h4> +<ul> +<li><strong>ip</strong>:网络协议地址IP</li> +<li><strong>ip4_addr</strong>:网络协议地址IPv4</li> +<li><strong>mac</strong>:mac地址</li> +<li><strong>url</strong>:url</li> +</ul> +<h4 id="4字符">4)字符</h4> +<ul> +<li><strong>ascii</strong>:ASCII</li> +<li><strong>boolean</strong>:Boolean</li> +<li><strong>endswith</strong>: 以&hellip;结尾</li> +<li><strong>contains</strong>:包含</li> +<li><strong>uppercase</strong>:大写</li> +</ul> +<h4 id="5格式">5)格式</h4> +<ul> +<li><strong>base64</strong>:Base64字符串</li> +<li><strong>base64url</strong>:Base64url字符串</li> +<li><strong>email</strong>:邮箱字符串</li> +<li><strong>json</strong>:JSON</li> +<li><strong>jwt</strong>:JSON Web Token</li> +<li><strong>latitude</strong>:纬度</li> +</ul> +<h4 id="6其它">6)其它</h4> +<ul> +<li><strong>len</strong>:长度</li> +<li><strong>max</strong>:最大值</li> +<li><strong>min</strong>:最小值</li> +<li><strong>required</strong>:字段为必须,不可空</li> +</ul> +<h4 id="7别名">7)别名</h4> +<ul> +<li><strong>iscolor</strong>:hexcolor|rgb|rgba|hsl|hsla</li> +<li><strong>country_code</strong>:iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric</li> +</ul> +<h2 id="案例">案例</h2> +<h3 id="简单验证">简单验证</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-playground/validator/v10&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,lte=10&#34;`</span> <span class="c1">// 姓名 非空,长度小于等于10 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Age</span> <span class="kt">int</span> <span class="s">`validate:&#34;required,gte=18,lte=50&#34;`</span> <span class="c1">// 年龄 非空,数字大于等于18,小于等于50 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Email</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,email&#34;`</span> <span class="c1">// 邮箱 非空,格式为email +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">FavouriteColor</span> <span class="kt">string</span> <span class="s">`validate:&#34;iscolor&#34;`</span> <span class="c1">// 喜欢的颜色 hexcolor|rgb|rgba|hsl|hsla的别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Password</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,gte=16,lte=22&#34;`</span> <span class="c1">// 密码 非空,长度大于等于16,小于等于22 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">RePassword</span> <span class="kt">string</span> <span class="s">`validate:&#34;required,gte=16,lte=22,eqfield=Password&#34;`</span> <span class="c1">// 确认密码 非空,长度大于等于16,小于等于22,必须和字段Password相同 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Hobbies</span> <span class="p">[]</span><span class="nx">Hobby</span> <span class="s">`validate:&#34;lte=5&#34;`</span> <span class="c1">// 多个爱好 长度小于等于5 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Hobby</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> <span class="s">`validate:&#34;lte=50&#34;`</span> <span class="c1">// 爱好名称 长度小于等于50 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">validate</span> <span class="o">*</span><span class="nx">validator</span><span class="p">.</span><span class="nx">Validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 该函数验证struct +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 不会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">validateStruct</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 该函数单度验证字段 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">validateVariable</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">validateStruct</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">hobby</span> <span class="o">:=</span> <span class="nx">Hobby</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;划水&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;张三&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Age</span><span class="p">:</span> <span class="mi">48</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Email</span><span class="p">:</span> <span class="s">&#34;hi.hunterji@gmail.com&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">FavouriteColor</span><span class="p">:</span> <span class="s">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="s">&#34;1234567890123456&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">RePassword</span><span class="p">:</span> <span class="s">&#34;1234567890123456&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Hobbies</span><span class="p">:</span> <span class="p">[]</span><span class="nx">Hobby</span><span class="p">{</span><span class="nx">hobby</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Struct</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">validateVariable</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">email</span> <span class="o">:=</span> <span class="s">&#34;hi.hunterji@gmail.com&#34;</span> <span class="c1">// 此处邮箱地址格式写的是错误的,会导致报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">email</span><span class="p">,</span> <span class="s">&#34;required,email&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义验证">自定义验证</h3> +<p>自定义验证可以自己创建一个校验的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 注册校验函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">ValidateMyVal</span><span class="p">(</span><span class="nx">fl</span> <span class="nx">validator</span><span class="p">.</span><span class="nx">FieldLevel</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fl</span><span class="p">.</span><span class="nf">Field</span><span class="p">().</span><span class="nf">String</span><span class="p">()</span> <span class="o">==</span> <span class="s">&#34;hello,world!&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后将其注册到validate上即可:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">validate</span><span class="p">.</span><span class="nf">RegisterValidation</span><span class="p">(</span><span class="s">&#34;is-hello&#34;</span><span class="p">,</span> <span class="nx">ValidateMyVal</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">s</span> <span class="o">:=</span> <span class="s">&#34;hello,kuari&#34;</span> <span class="c1">// 跟校验函数中的字符串不同,因此此处会报错 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="s">&#34;is-hello&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>自定义校验可以满足开发过程中的特殊场景,通过制定规范的校验标准,可以推进团队的协作和开发效率。</p> +<h2 id="最后">最后</h2> +<p>至此便是对于<code>validator</code>的介绍了。本文篇幅较短,管中窥豹而已,基本可以满足简单场景的使用。以及本文的案例也是基于官方的案例改的,让其稍微接地气点。若有兴趣的小伙伴还是建议去完整看一下官方的文档和案例,多样的用法可以满足多样的场景,在满足代码的健壮性的同时也能确保代码的优美。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/go-playground/validator" target="_blank" rel="noopener" + >go-playground/validator</a></li> +</ul> + + + + Golang实现农历转换阳历 + https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/ + Tue, 31 Aug 2021 09:08:57 +0000 + + https://blog.hunterji.com/p/golang%E5%AE%9E%E7%8E%B0%E5%86%9C%E5%8E%86%E8%BD%AC%E6%8D%A2%E9%98%B3%E5%8E%86/ + <h2 id="前言">前言</h2> +<p>因为项目需求,需要去检测用户的农历生日。虽然后来找到了合适的库,但是首先先解释下<code>农历</code>的定义,也是去了解才知道,原来农历不是阴历。</p> +<p>农历属于阴阳合历,其年份分为平年和闰年。平年为十二个月,闰年为十三个月。月份分为大月和小月,大月三十天,小月二十九天,其平均历月等于一个朔望月。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Go 1.16</li> +<li>github.com/nosixtools/solarlunar 0.0.0</li> +</ul> +<h2 id="库">库</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">nosixtools</span><span class="o">/</span><span class="nx">solarlunar</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>该库支持1900~2049年。所以项目要跑到2049年后的童鞋就要注意&hellip;&hellip;</p> +<p>当然,该库还支持阳历转农历、节假日计算等,有兴趣大家可以自行去了解下。</p> +<h2 id="使用">使用</h2> +<h3 id="判断闰年">判断闰年</h3> +<p>该库不支持闰年判断,所以需要自己去实现闰年的判断,其参数类型为<code>Boolean</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">year</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">result</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">year</span><span class="o">%</span><span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">year</span><span class="o">%</span><span class="mi">100</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">year</span><span class="o">%</span><span class="mi">400</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="转换">转换</h3> +<p>需要转换的阳历日期格式是固定的,是<code>2006-01-02</code>。此处以农历<code>2021-07-17</code>为例。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lunarDate</span> <span class="o">:=</span> <span class="s">&#34;2021-07-17&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">solarlunar</span><span class="p">.</span><span class="nf">LunarToSolar</span><span class="p">(</span><span class="nx">lunarDate</span><span class="p">,</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Year</span><span class="p">())))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>输出为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="mi">2021</span><span class="o">-</span><span class="mi">08</span><span class="o">-</span><span class="mi">24</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="全部代码">全部代码</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/nosixtools/solarlunar&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lunarDate</span> <span class="o">:=</span> <span class="s">&#34;2021-07-17&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">solarlunar</span><span class="p">.</span><span class="nf">LunarToSolar</span><span class="p">(</span><span class="nx">lunarDate</span><span class="p">,</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Year</span><span class="p">())))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IsALeapYear</span><span class="p">(</span><span class="nx">year</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">result</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">year</span><span class="o">%</span><span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">year</span><span class="o">%</span><span class="mi">100</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">year</span><span class="o">%</span><span class="mi">400</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + go中json解析报错invalid character '\\b' after top-level value + https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/ + Wed, 11 Aug 2021 13:38:49 +0000 + + https://blog.hunterji.com/p/go%E4%B8%ADjson%E8%A7%A3%E6%9E%90%E6%8A%A5%E9%94%99invalid-character-%5C%5Cb-after-top-level-value/ + <h2 id="报错">报错</h2> +<p>在<code>Golang</code>中<code>json</code>解析时报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">invalid character &#39;\\b&#39; after top-level value +</span></span></code></pre></td></tr></table> +</div> +</div><p>代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">result</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">response</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="分析与排错">分析与排错</h2> +<p>首先将<code>result</code>打印出来,发现并无异常,其标点符号也没有问题。</p> +<p>然后查看网上现有解决方案的帖子基本试了下,起码对于我来说并不适用,概括下方案:</p> +<ol> +<li>遍历然后过滤,最后重组;</li> +<li>遍历,使用SetEscapeHTML(false)禁用转义符;</li> +<li>编码;</li> +<li>&hellip;</li> +</ol> +<p>最后对比代码中获取到的字符产长度和手动复制所见的字符串的长度,发现确实代码中字符长度不同,其长度是80,而手动复制的字符串的长度是72。</p> +<h2 id="解决">解决</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">strings</span><span class="p">.</span><span class="nf">ReplaceAll</span><span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="s">&#34;\b&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>就挺简单的&hellip;&hellip;</p> + + + + Golang AES-256-CBC加密和解密 + https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/ + Wed, 11 Aug 2021 13:13:12 +0000 + + https://blog.hunterji.com/p/golang-aes-256-cbc%E5%8A%A0%E5%AF%86%E5%92%8C%E8%A7%A3%E5%AF%86/ + <h2 id="前言">前言</h2> +<p>项目开发中遇到该问题,网上的文章太乱,为了节省下次踩坑时间,特此记录。</p> +<h2 id="加解密">加解密</h2> +<h3 id="填充函数">填充函数</h3> +<p>该函数在加解密中都需要用到。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">PKCS5Padding</span><span class="p">(</span><span class="nx">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padding</span> <span class="o">:=</span> <span class="nx">blockSize</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">blockSize</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padText</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="nb">byte</span><span class="p">(</span><span class="nx">padding</span><span class="p">)},</span> <span class="nx">padding</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">padText</span><span class="o">...</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="加密">加密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Ase256Encrypt</span><span class="p">(</span><span class="nx">plaintext</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bKey</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bIV</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bPlaintext</span> <span class="o">:=</span> <span class="nf">PKCS5Padding</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">plaintext</span><span class="p">),</span> <span class="nx">blockSize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">(</span><span class="nx">bKey</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bPlaintext</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCEncrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="nx">bIV</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">bPlaintext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">EncodeToString</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解密">解密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">DecodeString</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;ciphertext is not a multiple of the block size&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCDecrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="全部代码">全部代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 填充 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">PKCS5Padding</span><span class="p">(</span><span class="nx">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padding</span> <span class="o">:=</span> <span class="nx">blockSize</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">blockSize</span> +</span></span><span class="line"><span class="cl"> <span class="nx">padText</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="nb">byte</span><span class="p">(</span><span class="nx">padding</span><span class="p">)},</span> <span class="nx">padding</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">append</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">padText</span><span class="o">...</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 加密 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Ase256</span><span class="p">(</span><span class="nx">plaintext</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">blockSize</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bKey</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bIV</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">bPlaintext</span> <span class="o">:=</span> <span class="nf">PKCS5Padding</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">plaintext</span><span class="p">),</span> <span class="nx">blockSize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">(</span><span class="nx">bKey</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">bPlaintext</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCEncrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="nx">bIV</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">bPlaintext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">EncodeToString</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解密 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">base64</span><span class="p">.</span><span class="nx">StdEncoding</span><span class="p">.</span><span class="nf">DecodeString</span><span class="p">(</span><span class="nx">cryptData</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">block</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">)</span><span class="o">%</span><span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;ciphertext is not a multiple of the block size&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span> <span class="o">:=</span> <span class="nx">cipher</span><span class="p">.</span><span class="nf">NewCBCDecrypter</span><span class="p">(</span><span class="nx">block</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">iv</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mode</span><span class="p">.</span><span class="nf">CryptBlocks</span><span class="p">(</span><span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">ciphertext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ciphertext</span><span class="p">,</span> <span class="nx">err</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="调用">调用</h2> +<h3 id="加密-1">加密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">result</span> <span class="o">:=</span> <span class="nf">Ase256Encrypt</span><span class="p">(</span><span class="s">&#34;&lt;需要加密的数据&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;key&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;iv&gt;&#34;</span><span class="p">,</span> <span class="nx">aes</span><span class="p">.</span><span class="nx">BlockSize</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解密-1">解密</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">result</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Aes256Decrypt</span><span class="p">(</span><span class="s">&#34;&lt;需要解密的数据&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;key&gt;&#34;</span><span class="p">,</span> <span class="s">&#34;&lt;iv&gt;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + sqlx建模、连接与使用 + https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/ + Fri, 25 Dec 2020 12:51:12 +0000 + + https://blog.hunterji.com/p/sqlx%E5%BB%BA%E6%A8%A1%E8%BF%9E%E6%8E%A5%E4%B8%8E%E4%BD%BF%E7%94%A8/ + <h2 id="前言">前言</h2> +<p>为什么要用<code>sqlx</code>而不是<code>gorm</code>呢?是因为<code>orm</code>学习成本比较高,当使用<code>python</code>时候需要使用<code>sqlalchemy</code>,遇到<code>go</code>就要换成<code>gorm</code>,换成别的语言就又有其他orm。而直接使用原生<code>sql</code>可以减少学习成本,适用于所有开发语言。其次,<code>gorm</code>本身支持软删除,但是其对软删除的支持上存在缺陷,在单条查询可以过滤软删除数据,但是在多条查询时无法有效过滤,就造成了有时候要手动过滤又有时候不要手动过滤,使用体验非常差。</p> +<p>因此此处考虑去使用<code>sqlx</code>来直接调用原生<code>sql</code>。</p> +<h2 id="安装">安装</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">go</span> <span class="nx">get</span> <span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">jmoiron</span><span class="o">/</span><span class="nx">sqlx</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="连接数据库">连接数据库</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">database</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="s">&#34;github.com/go-sql-driver/mysql&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/jmoiron/sqlx&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">DBConnect</span><span class="p">()</span> <span class="o">*</span><span class="nx">sqlx</span><span class="p">.</span><span class="nx">DB</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">env</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;NODE_ENV&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">password</span><span class="p">,</span> <span class="nx">dbname</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 使用环境变量来切换生产和开发环境 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">env</span> <span class="o">==</span> <span class="s">&#34;production&#34;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">host</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbHost&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbUser&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbPassword&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dbname</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;dbname&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">host</span> <span class="p">=</span> <span class="s">&#34;&lt;your-host&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="s">&#34;&lt;your-port&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span> <span class="p">=</span> <span class="s">&#34;&lt;your-user&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="s">&#34;&lt;your-password&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dbname</span> <span class="p">=</span> <span class="s">&#34;&lt;your-db-name&gt;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">dbConfig</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s:%s@tcp(%s:%s)/%s?parseTime=true&#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">password</span><span class="p">,</span> <span class="nx">host</span><span class="p">,</span> <span class="nx">port</span><span class="p">,</span> <span class="nx">dbname</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">db</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlx</span><span class="p">.</span><span class="nf">Connect</span><span class="p">(</span><span class="s">&#34;mysql&#34;</span><span class="p">,</span> <span class="nx">dbConfig</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="s">&#34;failed to connect database&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">db</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="创建表和调用">创建表和调用</h2> +<h3 id="创建表">创建表</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">test_gin</span><span class="p">.</span><span class="n">todo</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">(</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">todo_id</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="n">auto_increment</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;todo标题&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;内容&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">user_id</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;用户id&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="k">timestamp</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;创建时间戳&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">updated_at</span><span class="w"> </span><span class="k">timestamp</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">update</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;更新时间戳&#39;</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">is_deleted</span><span class="w"> </span><span class="n">tinyint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">comment</span><span class="w"> </span><span class="s1">&#39;是否被删除,0:未删,1:已删&#39;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">);</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建struct">创建struct</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Todo</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TodoID</span> <span class="kt">int</span> <span class="s">`db:&#34;todo_id&#34; json:&#34;todo_id,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`db:&#34;title&#34; json:&#34;title,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`db:&#34;content&#34; json:&#34;content,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UserID</span> <span class="kt">int</span> <span class="s">`db:&#34;user_id&#34; json:&#34;user_id,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">CreatedAt</span> <span class="kt">string</span> <span class="s">`db:&#34;created_at&#34; json:&#34;created_at,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UpdatedAt</span> <span class="kt">string</span> <span class="s">`db:&#34;updated_at&#34; json:&#34;updated_at,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">IsDeleted</span> <span class="kt">bool</span> <span class="s">`db:&#34;is_deleted&#34; json:&#34;is_deleted,omitempty&#34;`</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="封装方法">封装方法</h3> +<p>此处以增删为例。封装常用方法是为了复用,封装时候使用害羞的代码。不需要为了封装而封装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 新增Todo +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">Todo</span><span class="p">)</span> <span class="nf">Add</span><span class="p">()</span> <span class="p">(</span><span class="nx">todoID</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 连接数据库 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 执行添加sql +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;insert into todo (title, content, user_id) value (?, ?, ?)&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">UserID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lastTodoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">result</span><span class="p">.</span><span class="nf">LastInsertId</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">todoID</span> <span class="p">=</span> <span class="nb">int</span><span class="p">(</span><span class="nx">lastTodoID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 删除Todo +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">Todo</span><span class="p">)</span> <span class="nf">Del</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;update todo set is_deleted = 0 where is_deleted = 0 and todo_id = ?&#34;</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">TodoID</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="视图中使用">视图中使用</h2> +<p>此处以新增接口为例。</p> +<h3 id="调用封装的方法">调用封装的方法</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">views</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testGin/models&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// addTodo.go -- post +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AddTodo</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// {&#34;title&#34;: &#34;hello&#34;, &#34;content&#34;: &#34;world&#34;} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">requestBody</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`json:&#34;content&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBind</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">requestBody</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数有误&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">todo</span> <span class="o">:=</span> <span class="nx">models</span><span class="p">.</span><span class="nx">Todo</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span><span class="p">:</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span><span class="p">:</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">UserID</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="c1">// 此处的1为假数据,此处应当从上下文获取请求用户的user_id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">todoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">todo</span><span class="p">.</span><span class="nf">Add</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;todo_id&#34;</span><span class="p">:</span> <span class="nx">todoID</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="直接使用">直接使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">views</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testGin/database&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// addTodo.go -- post +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AddTodo</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// {&#34;title&#34;: &#34;hello&#34;, &#34;content&#34;: &#34;world&#34;} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">requestBody</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Content</span> <span class="kt">string</span> <span class="s">`json:&#34;content&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBind</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">requestBody</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数有误&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 连接数据库 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">db</span> <span class="o">:=</span> <span class="nx">database</span><span class="p">.</span><span class="nf">DBConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 执行添加sql +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">tx</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">MustBegin</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">MustExec</span><span class="p">(</span><span class="s">&#34;insert into todo (title, content, user_id) value (?, ?, ?)&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Title</span><span class="p">,</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lastTodoID</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">result</span><span class="p">.</span><span class="nf">LastInsertId</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tx</span><span class="p">.</span><span class="nf">Commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;todo_id&#34;</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="nx">lastTodoID</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + gin中间件和鉴权 + https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/ + Fri, 18 Dec 2020 14:02:30 +0000 + + https://blog.hunterji.com/p/gin%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%92%8C%E9%89%B4%E6%9D%83/ + <h2 id="前言">前言</h2> +<p><code>gin</code>的中间件的使用场景非常广泛,此处主要介绍如何使用其来完成常见场景下的鉴权。</p> +<h2 id="官方文档">官方文档</h2> +<p>官方文档列出了如下几种使用方式:</p> +<ul> +<li><a class="link" href="https://github.com/gin-gonic/gin#using-middleware" target="_blank" rel="noopener" + >使用中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#custom-middleware" target="_blank" rel="noopener" + >定制中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#using-basicauth-middleware" target="_blank" rel="noopener" + >使用基础认证的中间件</a></li> +<li><a class="link" href="https://github.com/gin-gonic/gin#goroutines-inside-a-middleware" target="_blank" rel="noopener" + >中间件使用协程</a></li> +</ul> +<h2 id="不同场景的鉴权实现">不同场景的鉴权实现</h2> +<h3 id="api-key">api key</h3> +<p>对于<code>api key</code>的方式需要设置白名单,对白名单外的请求进行<code>token</code>检测。此中间件在处理请求被处理之前对请求进行拦截,验证token,因此可在此处利用<code>gin.Context</code>来设置上下文,如请求所属用户的用户信息等。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">middleware</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/url&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;strings&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">whiteList</span><span class="p">()</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;/ping&#34;</span><span class="p">:</span> <span class="s">&#34;GET&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">withinWhiteList</span><span class="p">(</span><span class="nx">url</span> <span class="o">*</span><span class="nx">url</span><span class="p">.</span><span class="nx">URL</span><span class="p">,</span> <span class="nx">method</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">target</span> <span class="o">:=</span> <span class="nf">whiteList</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">queryUrl</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">url</span><span class="p">),</span> <span class="s">&#34;?&#34;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">target</span><span class="p">[</span><span class="nx">queryUrl</span><span class="p">];</span> <span class="nx">ok</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">target</span><span class="p">[</span><span class="nx">queryUrl</span><span class="p">]</span> <span class="o">==</span> <span class="nx">method</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Authorize</span><span class="p">()</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">HandlerFunc</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">type</span> <span class="nx">QueryToken</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Token</span> <span class="kt">string</span> <span class="s">`binding:&#34;required,len=3&#34; form:&#34;token&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当路由不在白名单内时进行token检测 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">!</span><span class="nf">withinWhiteList</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">URL</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">Method</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">queryToken</span> <span class="nx">QueryToken</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindQuery</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">queryToken</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">AbortWithStatusJSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">,</span> <span class="s">&#34;user&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="路由权限">路由权限</h3> +<h4 id="1说明">1)说明</h4> +<p>对于请求的处理,需要去验证是否对其请求的路径拥有访问权限。</p> +<p>首先看一下<code>gin</code>的路由设置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">group</span> <span class="o">*</span><span class="nx">RouterGroup</span><span class="p">)</span> <span class="nf">POST</span><span class="p">(</span><span class="nx">relativePath</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">handlers</span> <span class="o">...</span><span class="nx">HandlerFunc</span><span class="p">)</span> <span class="nx">IRoutes</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>其参数为<code>...HandlerFunc</code>,其解释为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">HandlerFunc</span> <span class="kd">func</span><span class="p">(</span><span class="o">*</span><span class="nx">Context</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">HandlerFunc</span> <span class="nx">defines</span> <span class="nx">the</span> <span class="nx">handler</span> <span class="nx">used</span> <span class="nx">by</span> <span class="nx">gin</span> <span class="nx">middleware</span> <span class="nx">as</span> <span class="k">return</span> <span class="nx">value</span><span class="p">.</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>所以此处可以通过定制中间件的方式实现一个路由权限处理。</p> +<p>当然此处的权限处理比较简单,使用角色直接去判断权限。如分为两个角色,管理员<code>admin</code>和普通用户<code>user</code>。</p> +<p>不过此处实现有个前提条件,就是如何拿到用户的角色呢?此处需要在上一步(<code>api key</code>)的实现中加上利用<code>gin.Context</code>设置角色:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">c</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">,</span> <span class="s">&#34;admin&#34;</span><span class="p">)</span> <span class="c1">// 可见上一步的代码,当然此处只是为了演示设置固定值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后在中间件中拿到角色并进行判断。</p> +<h4 id="2路由权限中间件">2)路由权限中间件</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">middleware</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;errors&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Permissions</span><span class="p">(</span><span class="nx">roles</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">HandlerFunc</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">permissionsErr</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 获取上下文中的用户角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">roleValue</span><span class="p">,</span> <span class="nx">exists</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;role&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">exists</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;获取用户信息失败&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">role</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">roleValue</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断请求的用户的角色是否属于设定角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">noAccess</span> <span class="o">:=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="nx">roles</span><span class="p">);</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">role</span> <span class="o">==</span> <span class="nx">roles</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">noAccess</span> <span class="p">=</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">noAccess</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;权限不够&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">permissionsErr</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">AbortWithStatusJSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">40001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Next</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3使用">3)使用</h4> +<p>在设置路由时候,添加该中间件,并设置白名单。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">r</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="nx">middleware</span><span class="p">.</span><span class="nf">Permissions</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;admin&#34;</span><span class="p">}),</span> <span class="nx">views</span><span class="p">.</span><span class="nx">AddTodo</span><span class="p">)</span> <span class="c1">// 添加中间件将会验证角色 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">r</span><span class="p">.</span><span class="nf">PUT</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="nx">views</span><span class="p">.</span><span class="nx">ModifyTodo</span><span class="p">)</span> <span class="c1">// 未添加中间件则不会验证角色 +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + go-Redis的发布与订阅 + https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/ + Fri, 27 Nov 2020 17:41:53 +0000 + + https://blog.hunterji.com/p/go-redis%E7%9A%84%E5%8F%91%E5%B8%83%E4%B8%8E%E8%AE%A2%E9%98%85/ + <h2 id="前言">前言</h2> +<p>在数据量较小的情况下,可以使用<code>Redis</code>来实现消息的发布与订阅,来代替<code>Kafka</code>。<code>Kafka</code>对于数据量大的场景下性能卓越,但是对于如此小场景时候,不仅运维成本提升,还用不上多少性能。</p> +<p>不过使用<code>Redis</code>的另一个弊端是消息不能堆积,一旦消费者节点没有消费消息,消息将会丢失。因此需要评估当下场景来选择适合的架构。</p> +<p>此处使用go-redis来实现<code>Redis</code>的发布与订阅。</p> +<h2 id="官方文档">官方文档</h2> +<p><a class="link" href="https://pkg.go.dev/github.com/go-redis/redis/v8#PubSub" target="_blank" rel="noopener" + >官方文档</a>有较为完整的例子:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;mychannel1&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Wait for confirmation that subscription is created before publishing anything. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Go channel which receives messages. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Publish a message. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;mychannel1&#34;</span><span class="p">,</span> <span class="s">&#34;hello&#34;</span><span class="p">).</span><span class="nf">Err</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">time</span><span class="p">.</span><span class="nf">AfterFunc</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// When pubsub is closed channel is closed too. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Consume messages. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="代码实现">代码实现</h2> +<p>分步讲解下具体实现代码。</p> +<h3 id="连接redis">连接redis</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="发布消息">发布消息</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="订阅消息">订阅消息</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整案例">完整案例</h2> +<p>此处分为一个发布节点和一个订阅节点来实现了简单的发布与订阅。</p> +<h3 id="消息发布节点">消息发布节点</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">channel</span> <span class="o">:=</span> <span class="s">&#34;hello&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">msgList</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;hello&#34;</span><span class="p">,</span> <span class="s">&#34;world&#34;</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 此处发了两个消息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">msgList</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nf">pubMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;已经发送%s到%s\n&#34;</span><span class="p">,</span> <span class="nx">msg</span><span class="p">,</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="消息订阅节点">消息订阅节点</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">redisConnect</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisUrl&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPort&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="s">&#34;RedisPass&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1">// use default DB +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">redisConnect</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">pubsub</span> <span class="o">:=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Receive</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nx">pubsub</span><span class="p">.</span><span class="nf">Channel</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">Channel</span><span class="p">,</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Payload</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">channel</span> <span class="o">:=</span> <span class="s">&#34;hello&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nf">subMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行结果">运行结果</h2> +<h3 id="消息发布节点输出">消息发布节点输出</h3> +<img src="../assets/go_redis_pub.png" style="zoom:50%;" /> +<h3 id="消息订阅节点输出">消息订阅节点输出</h3> +<img src="../assets/go_redis_sub.png" style="zoom:50%;" /> + + + go单元测试 + https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + Fri, 20 Nov 2020 15:07:06 +0000 + + https://blog.hunterji.com/p/go%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + <h2 id="前言">前言</h2> +<p>想要写出好的 Go 程序,单元测试是很重要的一部分。 <code>testing</code> 包为提供了编写单元测试所需的工具,写好单元测试后,可以通过 <code>go test</code> 命令运行测试。</p> +<h2 id="规则">规则</h2> +<p><code>testing</code> 为 Go 语言 package 提供自动化测试的支持。通过 <code>go test</code> 命令,能够自动执行如下形式的任何函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestXxx</span><span class="p">(</span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 <code>TestXxx</code> 函数,如上所述。 将该文件放在与被测试文件相同的包中。该文件将被排除在正常的程序包之外,但在运行 <code>go test</code> 命令时将被包含。</p> +<h2 id="代码结构">代码结构</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── go.mod +</span></span><span class="line"><span class="cl">├── intMinBasicDriven_test.go +</span></span><span class="line"><span class="cl">├── intMinBasic_test.go +</span></span><span class="line"><span class="cl">└── main.go +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="第一个单元测试">第一个单元测试</h2> +<h3 id="要被测试的代码">要被测试的代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// main.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IntMin</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 返回a与b中的较小值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">a</span> <span class="p">&lt;</span> <span class="nx">b</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">a</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">b</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="测试代码">测试代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// intMinBasic_test.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestIntMinBasic</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ans</span> <span class="o">:=</span> <span class="nf">IntMin</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ans</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// t.Error* 会报告测试失败的信息,然后继续运行测试。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// t.Fail* 会报告测试失败的信息,然后立即终止测试。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;IntMin(2, -2) = %d; want -2&#34;</span><span class="p">,</span> <span class="nx">ans</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行测试">运行测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go <span class="nb">test</span> +</span></span><span class="line"><span class="cl">// 输出 +</span></span><span class="line"><span class="cl">ok heihei 0.385s +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="table-driven-test">Table-Driven Test</h2> +<p>单元测试可以重复,所以会经常使用 <em>表驱动</em> 风格编写单元测试, 表中列出了输入数据,预期输出,使用循环,遍历并执行测试逻辑。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// intMinBasicDriven_test.go +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestIntMinTableDriven</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">tests</span> <span class="p">=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">want</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="p">}{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// t.Run 可以运行一个 “subtests” 子测试,一个子测试对应表中一行数据。 运行 go test -v 时,他们会分开显示。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tt</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tests</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">testname</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%d,%d&#34;</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">a</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">testname</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ans</span> <span class="o">:=</span> <span class="nf">IntMin</span><span class="p">(</span><span class="nx">tt</span><span class="p">.</span><span class="nx">a</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">ans</span> <span class="o">!=</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;got %d, want %d&#34;</span><span class="p">,</span> <span class="nx">ans</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="运行代码">运行代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go <span class="nb">test</span> -v +</span></span><span class="line"><span class="cl">// <span class="nv">输出</span> +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN <span class="nv">TestIntMinTableDriven</span> +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/0,1 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/1,0 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/2,-2 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/0,-1 +</span></span><span class="line"><span class="cl"><span class="o">===</span> RUN TestIntMinTableDriven/-1,0 +</span></span><span class="line"><span class="cl">--- PASS: TestIntMinTableDriven <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/0,1 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/1,0 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/2,-2 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/0,-1 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl"> --- PASS: TestIntMinTableDriven/-1,0 <span class="o">(</span>0.00s<span class="o">)</span> +</span></span><span class="line"><span class="cl">PASS +</span></span><span class="line"><span class="cl">ok heihei 0.566s +</span></span></code></pre></td></tr></table> +</div> +</div> + + + gin的http单元测试 + https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + Fri, 20 Nov 2020 14:47:04 +0000 + + https://blog.hunterji.com/p/gin%E7%9A%84http%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/ + <h2 id="前言">前言</h2> +<p>Go 标准库专门提供了 <code>net/http/httptest</code> 包专门用于进行 http Web 开发测试。</p> +<p>此处基于gin来实现http的单元测试。</p> +<h2 id="get请求">GET请求</h2> +<p>此处使用http单元测试对<code>/ping</code>请求测试,其正常会返回字符串<code>pong</code>,响应码为<code>200</code>。</p> +<h3 id="web应用">web应用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setupRouter</span><span class="p">()</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Engine</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/ping&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="s">&#34;pong&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="http单元测试">http单元测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http/httptest&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;testing&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/stretchr/testify/assert&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestPingRoute</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建http server并发起请求 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">,</span> <span class="s">&#34;/ping&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> <span class="c1">// 断言响应码为200 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;pong&#34;</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span> <span class="c1">// 断言响应为&#34;pong&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="postputdelete请求">POST/PUT/DELETE请求</h2> +<p>post、put、delete请求处理相似,都是处理其request body,因此此处只以post为例。</p> +<p>此处对<code>/todo</code>进行测试,其正常返回为<code>json</code>,其中<code>code</code>为<code>20000</code>,响应码为<code>200</code>。</p> +<h3 id="web应用-1">web应用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setupRouter</span><span class="p">()</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Engine</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">type</span> <span class="nx">ToDo</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TodoId</span> <span class="kt">int</span> <span class="s">`binding:&#34;required&#34; json:&#34;todo_id&#34;`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">todo</span> <span class="nx">ToDo</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindJSON</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">todo</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;message&#34;</span><span class="p">:</span> <span class="s">&#34;参数不全&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 处理代码,此处以打印为例,省略处理... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">todo</span><span class="p">.</span><span class="nx">TodoId</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;code&#34;</span><span class="p">:</span> <span class="mi">20000</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nf">setupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="http单元测试-1">http单元测试</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestTodoCreate</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 请求方法 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">method</span> <span class="o">:=</span> <span class="s">&#34;POST&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 请求路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">urlStr</span> <span class="o">:=</span> <span class="s">&#34;/todo&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// request body +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">body</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;title&#34;</span><span class="p">:</span> <span class="s">&#34;hello&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonByte</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">urlStr</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">jsonByte</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nx">routers</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> <span class="c1">// 判断响应码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">response</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">()),</span> <span class="o">&amp;</span><span class="nx">response</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">response</span><span class="p">[</span><span class="s">&#34;code&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">20000</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="c1">// 判断自定义状态码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="封装测试请求">封装测试请求</h2> +<p>由于http单元测试代码中存在较多重复,因此此处封装重复代码。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">tests</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">TestConfig</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Url</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Method</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Body</span> <span class="kd">interface</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">tc</span> <span class="o">*</span><span class="nx">TestConfig</span><span class="p">)</span> <span class="nf">Request</span><span class="p">()</span> <span class="o">*</span><span class="nx">httptest</span><span class="p">.</span><span class="nx">ResponseRecorder</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Body</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonByte</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="p">=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Url</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">jsonByte</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="p">=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">Url</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">:=</span> <span class="nx">routers</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">w</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="封装后的单元测试">封装后的单元测试</h2> +<p>此处以post请求为例。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAddTag</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 新增tag +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">addTestConfig</span> <span class="nx">tests</span><span class="p">.</span><span class="nx">TestConfig</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Method</span> <span class="p">=</span> <span class="s">&#34;POST&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Url</span> <span class="p">=</span> <span class="s">&#34;/tag&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nx">Body</span> <span class="p">=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;tag_name&#34;</span><span class="p">:</span> <span class="s">&#34;HelloHello&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="nx">addTestConfig</span><span class="p">.</span><span class="nf">Request</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">addResponse</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">String</span><span class="p">()),</span> <span class="o">&amp;</span><span class="nx">addResponse</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">addResponse</span><span class="p">[</span><span class="s">&#34;code&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">20000</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">lastInsertTagId</span><span class="p">,</span> <span class="nx">exits</span> <span class="o">:=</span> <span class="nx">addResponse</span><span class="p">[</span><span class="s">&#34;tag_id&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">exits</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Golang使用JSON格式存取Redis + https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/ + Tue, 11 Aug 2020 18:42:42 +0000 + + https://blog.hunterji.com/p/golang%E4%BD%BF%E7%94%A8json%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%8F%96redis/ + <h2 id="前言">前言</h2> +<p>对于<code>Golang</code>操作<code>Redis</code>,此处使用<code>github.com/go-redis/redis</code>。</p> +<h2 id="操作">操作</h2> +<h3 id="连接redis服务器">连接redis服务器</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">redis</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;context&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsoniter</span> <span class="s">&#34;github.com/json-iterator/go&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BaseClient</span><span class="p">()</span> <span class="p">(</span><span class="nx">rdb</span> <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">redisServer</span> <span class="o">:=</span> <span class="s">&#34;redis_server&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span> <span class="o">:=</span> <span class="s">&#34;redis_port&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">password</span> <span class="o">:=</span> <span class="s">&#34;redis_password&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="p">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Addr</span><span class="p">:</span> <span class="nx">redisServer</span> <span class="o">+</span> <span class="s">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Password</span><span class="p">:</span> <span class="nx">password</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">DB</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>###存储</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">SetJson</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">value</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{},</span> <span class="nx">expiration</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">BaseClient</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">valueString</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">jsoniter</span><span class="p">.</span><span class="nf">MarshalToString</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">valueString</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="nx">expiration</span><span class="p">)</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">).</span><span class="nf">Err</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>调用</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">SetJson</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">,</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;name&#34;</span><span class="p">:</span> <span class="s">&#34;tom&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;age&#34;</span><span class="p">:</span> <span class="mi">12</span> +</span></span><span class="line"><span class="cl"> <span class="p">}),</span> +</span></span><span class="line"><span class="cl"> <span class="mi">60</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>###读取</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">value</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">rdb</span> <span class="o">:=</span> <span class="nf">BaseClient</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">rdb</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">key</span><span class="p">).</span><span class="nf">Result</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>调用</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Age</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">redis</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">user</span> <span class="nx">User</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">value</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>使用<code>JSON</code>格式存储与读取其实就是对目标数据在存储前和读取后进行格式转换。</p> + + + + Go生成6位随机数 + https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/ + Tue, 11 Aug 2020 09:08:20 +0000 + + https://blog.hunterji.com/p/go%E7%94%9F%E6%88%906%E4%BD%8D%E9%9A%8F%E6%9C%BA%E6%95%B0/ + <div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;math/rand&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;strconv&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">CreateVerifyCode</span><span class="p">()</span> <span class="p">(</span><span class="nx">verifyCode</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">min</span> <span class="o">:=</span> <span class="mi">100000</span> +</span></span><span class="line"><span class="cl"> <span class="nx">max</span> <span class="o">:=</span> <span class="mi">999999</span> +</span></span><span class="line"><span class="cl"> <span class="nx">verifyCode</span> <span class="p">=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Itoa</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="nx">max</span><span class="o">-</span><span class="nx">min</span><span class="p">)</span> <span class="o">+</span> <span class="nx">min</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Go http请求报错x509 certificate signed by unknown authority + https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/ + Tue, 04 Aug 2020 09:58:55 +0000 + + https://blog.hunterji.com/p/go-http%E8%AF%B7%E6%B1%82%E6%8A%A5%E9%94%99x509-certificate-signed-by-unknown-authority/ + <h2 id="报错">报错</h2> +<p>在Go中<code>POST</code>请求时报错</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">x509: certificate signed by unknown authority +</span></span></code></pre></td></tr></table> +</div> +</div><p>即无法检验证书。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Handle</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>跳过校验即可。此处引入<code>&quot;crypto/tls&quot;</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;crypto/tls&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Handle</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">tr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Transport</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">TLSClientConfig</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">tls</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="nx">InsecureSkipVerify</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">client</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Timeout</span><span class="p">:</span> <span class="mi">15</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Transport</span><span class="p">:</span> <span class="nx">tr</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/golang/page/1/index.html b/tags/golang/page/1/index.html new file mode 100644 index 0000000..4ee041d --- /dev/null +++ b/tags/golang/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/golang/ + + + + + + diff --git a/tags/golang/page/2/index.html b/tags/golang/page/2/index.html new file mode 100644 index 0000000..b84129c --- /dev/null +++ b/tags/golang/page/2/index.html @@ -0,0 +1,552 @@ + + + + +Tag: Golang - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

13 pages

+

Golang

+ +
+
+
+ +
+ + + +
+ + + + + +
+
+ + + + + diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..2695f63 --- /dev/null +++ b/tags/index.html @@ -0,0 +1,674 @@ + + + + +Tags + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

18 pages

+

Tags

+ +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/tags/index.xml b/tags/index.xml new file mode 100644 index 0000000..c3e210a --- /dev/null +++ b/tags/index.xml @@ -0,0 +1,155 @@ + + + + Tags on 开发者小橙 + https://blog.hunterji.com/tags/ + Recent content in Tags on 开发者小橙 + Hugo -- gohugo.io + en-us + Wed, 24 Jan 2024 23:53:17 +0000 + 独立开发 + https://blog.hunterji.com/tags/%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91/ + Wed, 24 Jan 2024 23:53:17 +0000 + + https://blog.hunterji.com/tags/%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91/ + + + + 日常分享 + https://blog.hunterji.com/tags/%E6%97%A5%E5%B8%B8%E5%88%86%E4%BA%AB/ + Tue, 05 Dec 2023 15:56:29 +0000 + + https://blog.hunterji.com/tags/%E6%97%A5%E5%B8%B8%E5%88%86%E4%BA%AB/ + + + + Rust + https://blog.hunterji.com/tags/rust/ + Thu, 26 Oct 2023 23:51:13 +0000 + + https://blog.hunterji.com/tags/rust/ + + + + WebAssembly + https://blog.hunterji.com/tags/webassembly/ + Thu, 26 Oct 2023 23:51:13 +0000 + + https://blog.hunterji.com/tags/webassembly/ + + + + Javascript + https://blog.hunterji.com/tags/javascript/ + Tue, 27 Jun 2023 00:11:12 +0000 + + https://blog.hunterji.com/tags/javascript/ + + + + TypeScript + https://blog.hunterji.com/tags/typescript/ + Fri, 17 Feb 2023 13:31:56 +0000 + + https://blog.hunterji.com/tags/typescript/ + + + + Golang + https://blog.hunterji.com/tags/golang/ + Thu, 09 Feb 2023 13:15:34 +0000 + + https://blog.hunterji.com/tags/golang/ + + + + CSS + https://blog.hunterji.com/tags/css/ + Sat, 28 Jan 2023 16:09:44 +0000 + + https://blog.hunterji.com/tags/css/ + + + + Vue + https://blog.hunterji.com/tags/vue/ + Sat, 28 Jan 2023 16:09:44 +0000 + + https://blog.hunterji.com/tags/vue/ + + + + Python + https://blog.hunterji.com/tags/python/ + Thu, 09 Jun 2022 22:57:54 +0000 + + https://blog.hunterji.com/tags/python/ + + + + Electron + https://blog.hunterji.com/tags/electron/ + Mon, 29 Nov 2021 17:29:14 +0000 + + https://blog.hunterji.com/tags/electron/ + + + + uniapp + https://blog.hunterji.com/tags/uniapp/ + Thu, 25 Nov 2021 17:07:55 +0000 + + https://blog.hunterji.com/tags/uniapp/ + + + + 前端 + https://blog.hunterji.com/tags/%E5%89%8D%E7%AB%AF/ + Thu, 25 Nov 2021 17:07:55 +0000 + + https://blog.hunterji.com/tags/%E5%89%8D%E7%AB%AF/ + + + + ObjectStorage + https://blog.hunterji.com/tags/objectstorage/ + Wed, 29 Sep 2021 09:22:22 +0000 + + https://blog.hunterji.com/tags/objectstorage/ + + + + Swift + https://blog.hunterji.com/tags/swift/ + Mon, 30 Aug 2021 14:29:02 +0000 + + https://blog.hunterji.com/tags/swift/ + + + + Node + https://blog.hunterji.com/tags/node/ + Thu, 29 Jul 2021 13:49:13 +0000 + + https://blog.hunterji.com/tags/node/ + + + + Docker + https://blog.hunterji.com/tags/docker/ + Wed, 14 Apr 2021 15:13:18 +0000 + + https://blog.hunterji.com/tags/docker/ + + + + Nginx + https://blog.hunterji.com/tags/nginx/ + Sat, 15 Aug 2020 22:07:58 +0000 + + https://blog.hunterji.com/tags/nginx/ + + + + + diff --git a/tags/javascript/index.html b/tags/javascript/index.html new file mode 100644 index 0000000..c2d8c3a --- /dev/null +++ b/tags/javascript/index.html @@ -0,0 +1,590 @@ + + + + +Tag: Javascript - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

5 pages

+

Javascript

+ +
+
+
+ +
+ + + + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/javascript/index.xml b/tags/javascript/index.xml new file mode 100644 index 0000000..a13eb3c --- /dev/null +++ b/tags/javascript/index.xml @@ -0,0 +1,936 @@ + + + + Javascript on 开发者小橙 + https://blog.hunterji.com/tags/javascript/ + Recent content in Javascript on 开发者小橙 + Hugo -- gohugo.io + en-us + Tue, 27 Jun 2023 00:11:12 +0000 + 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + Tue, 27 Jun 2023 00:11:12 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(二)——DOM和类型转换》</a>中,描述了使用Rust操作DOM,并实现Rust与JS类型转换的多种方法。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>基于上一篇文章中,Rust与JS的类型转换的多种方法,本篇文章继续深入Rust与JS的交互。</p> +<p>首先,Rust与JS的类型转换,可以实现变量的传递,那么变量是要用在哪里呢?那必然是函数了!</p> +<p>所以,本篇文章来讲述一下Rust与JS的函数相互调用,基于此,可以实现大量日常功能开发。</p> +<p>并且,还将会讲述一下,如何导出Rust的struct给JS调用。</p> +<p>是的,没错,在JS调用Rust的struct!一开始看到这个功能的时候,我的脑子是有点炸裂的&hellip;&hellip;😳</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="函数的相互调用">函数的相互调用</h2> +<h3 id="js调用rust函数">JS调用Rust函数</h3> +<p>其实,在本系列文章的第一篇中,就是使用的JS调用Rust函数作为案例来演示的,这里依然以此为例,主要讲一下要点。</p> +<p>首先,声明一个Rust函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处需要注意的要点如下:</p> +<ul> +<li>引入<code>wasm_bindgen</code></li> +<li>声明一个函数,使用<code>pub</code>声明</li> +<li>在函数上使用<code>#[wasm_bindgen]</code>宏来将Rust函数导出为WebAssembly模块的函数</li> +</ul> +<p>接着,编译成wasm文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,在JS中调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server,在浏览器的控制台中可以看到<code>the result from rust is: 3</code>,表明调用成功!</p> +<h3 id="rust调用js函数">Rust调用JS函数</h3> +<p>###1 指定JS对象</p> +<p>在Rust中调用JS函数,需要进行指定JS对象,也就是说,得明确告诉Rust,这个JS函数是从JS哪儿拿来的用的。</p> +<p>主要在于下面两个方式:</p> +<ul> +<li><em><strong>js_namespace</strong></em>: 是一个可选的属性,用于指定一个JavaScript命名空间,其中包含将要在wasm模块中导出的函数。如果没有指定<code>js_namespace</code>,则所有的导出函数将被放置在全局命名空间下。</li> +<li><em><strong>js_name</strong></em>: 是另一个可选属性,它用于指定JavaScript中的函数名称。如果没有指定<code>js_name</code>,则导出函数的名称将与Rust中的函数名称相同。</li> +</ul> +<p>###2 JS原生函数</p> +<p>对于一些JS原生函数,在Rust中,需要去寻找替代方案,比如我们上一篇文章中讲的<code>console.log()</code>函数,是不是觉得好麻烦啊!</p> +<p>那么,你想直接在Rust中调用JS原生函数吗?!</p> +<p>此处,就以<code>console.log()</code>函数为例,直接在Rust中引入并调用,免去替代方案的烦恼。</p> +<p>首先,给出Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如上代码中,<code>call_js_func</code>函数,顾名思义,此处是调用了js函数,并传入参数<code>hello, javascript!</code>。</p> +<p>那么,<code>call_js_func</code>函数上方的代码,我们来一步步解析一下:</p> +<ol> +<li>第一行代码<code>#[wasm_bindgen]</code>是Rust的属性,它告诉编译器将函数导出为WebAssembly模块</li> +<li><code>extern &quot;C&quot;</code>是C语言调用约定,它告诉Rust编译器将函数导出为C语言函数</li> +<li><code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的console对象</li> +<li><code>fn log(message: &amp;str)</code>是一个Rust函数,它接受一个字符串参数,并将其打印到JavaScript中的console对象中</li> +</ol> +<p>此处与JS交互的关键是<code>js_namespace</code>。在Rust中,<code>js_namespace</code>是用于指定JavaScript命名空间的属性。在WebAssembly中,我们可以通过它将函数绑定到JavaScript中的对象上。</p> +<p>在上述代码中,<code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的<code>console</code>对象。这意味着在JS中使用<code>console.log()</code>函数来调用Rust中的<code>log()</code>函数。</p> +<p>因此,类似的原生函数,都可以使用该方法来实现调用。</p> +<p>最后,我们在JS中调用下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">call_js_func</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">call_js_func</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以在浏览器的控制台中看到<code>hello, javascript!</code>。妙啊!</p> +<p>其实对于<code>console.log()</code>而言,还有另一种调用方式,那就是使用<code>js_namespace</code>和<code>js_name</code>同时指定。</p> +<p>或许,你会问,这有什么不同吗?是的,这有些不同。</p> +<p>不知道你是否发现,当前这个案例中,指定了<code>js_namespace</code>为<code>console</code>,但是真实执行的函数是<code>log()</code>,那么这个<code>log</code>函数的指定,其实是体现在Rust中同样的函数名<code>log</code>。也就是说,该案例的<code>log()</code>就是<code>console.log()</code>中的<code>log()</code>。</p> +<p>我们来换个名字看看,将原来的<code>log()</code>换成<code>log2()</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log2</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log2</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译后去控制台看看,就会看到报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">TypeError: console.log2 is not a function +</span></span></code></pre></td></tr></table> +</div> +</div><p>因此,当我们使用<code>js_namespace</code>和<code>js_name</code>结合的方式,在此处是可以进行自定义函数名的。</p> +<p>看一下Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console, js_name = log)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log_str</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log_str</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,重新定义了一个函数<code>log_str</code>,但是其指定了<code>js_namespace = console</code>和<code>js_name = log</code>,那么此处,就可以使用自定义的函数名。</p> +<p>直接编译后,在控制台看一下,可以直接看到正常输出:<code>hello, javascript!</code>。</p> +<p>总结一下,如果没有指定<code>js_name</code>,则 Rust 函数名称将用作 JS 函数名称。</p> +<p>###3 自定义JS函数</p> +<p>在一定场景下,需要使用Rust调用JS函数,比如对于一些对于JS而言更有优势的场景——用JS操作DOM,用Rust计算。</p> +<p>首先,创建一个文件<code>index.js</code>,写入一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">addIt</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当前的文件结构关系如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── Cargo.lock +</span></span><span class="line"><span class="cl">├── Cargo.toml +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── index.html +</span></span><span class="line"><span class="cl">├── index.js +</span></span><span class="line"><span class="cl">├── pkg +</span></span><span class="line"><span class="cl">│   ├── README.md +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.js +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">│   └── package.json +</span></span><span class="line"><span class="cl">├── src +</span></span><span class="line"><span class="cl">│   └── lib.rs +</span></span><span class="line"><span class="cl">└── target +</span></span><span class="line"><span class="cl"> ├── CACHEDIR.TAG +</span></span><span class="line"><span class="cl"> ├── debug +</span></span><span class="line"><span class="cl"> ├── release +</span></span><span class="line"><span class="cl"> └── wasm32-unknown-unknown +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>index.js</code>和<code>lib.rs</code>,以及<code>hello_wasm_bg.wasm</code>都是不在同一级别的,<code>index.js</code>都在其它两个文件的上一级。记住这个机构关系!</p> +<p>然后,在<code>lib.rs</code>中,指定函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl">##<span class="p">.</span><span class="o">/</span><span class="n">index</span><span class="p">.</span><span class="n">js</span><span class="s">&#34;)] +</span></span></span><span class="line"><span class="cl"><span class="s">extern &#34;</span><span class="n">C</span><span class="s">&#34; { +</span></span></span><span class="line"><span class="cl"><span class="s"> fn addIt(m: i32, n: i32) -&gt; i32; +</span></span></span><span class="line"><span class="cl"><span class="s">} +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>raw_module = &quot;../index.js&quot;</code>的意思是,指定对应的<code>index.js</code>文件,大家应该清楚,此处指定的是刚刚创建的<code>index.js</code>。<code>raw_module</code>的作用就是用来指定js文件的。</p> +<p>这段代码在前端,可以等同于:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addIt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;../index.js&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样在前端都不用引入了,直接在Rust中引入了,感觉还有点奇妙的。</p> +<p>接着,在Rust调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addIt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在前端调用,编译后,在浏览器的控制台中可以看到输出结果了!</p> +<p>总结一下,这里有几个注意点:</p> +<ol> +<li>JS的函数必须要export,否则将无法调用;</li> +<li><code>raw_module</code>只能用来指定相对路径,并且,大家可以在浏览器的控制台中注意到,此处的<code>../</code>的相对路径,其实是以wasm文件而言的相对路径,这里一定要注意呀!</li> +</ol> +<h2 id="js调用rust的struct">JS调用Rust的struct</h2> +<p>现在,来点炸裂的,JS调用Rust的struct?!</p> +<p>JS中连struct都没有,这玩意儿导出来会是什么样,得怎么在JS中调用呢?!</p> +<p>首先,定义一个struct,并且声明几个方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(constructor)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_user</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;name is : </span><span class="si">{}</span><span class="s">, age is : </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="p">).</span><span class="n">as_str</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">set_age</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">age</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,声明了一个struct名为<code>User</code>,包含<code>name</code>和<code>age</code>两个字段,并声明了<code>new</code>、<code>print_user</code>和<code>set_age</code>方法。</p> +<p>其中还有一个未见过的<code>#[wasm_bindgen(constructor)]</code>,<code>constructor</code>用于指示被绑定的函数实际上应该转换为调用 JavaScript 中的 new 运算符。或许你还不太清晰,继续看下去,你就会明白了。</p> +<p>接着,在JS中调用这个struct,和其方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">addIt2</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">User</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;kuari&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">set_age</span><span class="p">(</span><span class="mi">21</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">print_user</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到,这里的用法就很熟悉了!</p> +<p>大概想一下,在Rust中要如何调用?也就是直接new一个——<code>User::new('kuari', 20)</code>。</p> +<p>此处在JS中,也是如此,先new一个!</p> +<p>然后很自然地调用struct的方法。</p> +<p>编译后,打开浏览器,可以在控制台看到输出:<code>name is : kuari, age is : 21</code>。</p> +<p>其实,或许大家会很好奇,起码我是非常好奇的,Rust的struct在JS中到底是一个怎样的存在呢?</p> +<p>这里直接添加一个<code>console.log(user)</code>,就可以在输出看到。那么到底在JS中是一个怎样的存在呢?请各位动手打印一下看看吧!:P</p> +<h2 id="总结">总结</h2> +<p>本篇文章中,主要讲述了Rust与JS的交互,体现在Rust与JS的相互调用,这是建立在<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >上一篇文章</a>中类型转换的基础上的。</p> +<p>Rust与JS的函数相互调用的学习成本还是较大的,而且对比Go写wasm,Rust的颗粒度是非常细的,几乎可以说是随心所欲了。</p> +<p>比较炸裂的就是Rust的struct导出给JS用,这对于Rust与JS的交互而言,还是非常棒的体验。</p> + + + + 原来浏览器原生支持JS复制到剪切板 + https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + Wed, 15 Mar 2023 22:39:35 +0000 + + https://blog.hunterji.com/p/%E5%8E%9F%E6%9D%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%94%9F%E6%94%AF%E6%8C%81js%E5%A4%8D%E5%88%B6%E5%88%B0%E5%89%AA%E5%88%87%E6%9D%BF/ + <h2 id="第三方库的痛苦">第三方库的痛苦</h2> +<p>在日常的前端开发中,经常需要将一些数据从网页上复制到剪切板中。而实现复制功能,第一时间想到的就是引入第三方库。</p> +<p>曾经过多不少第三方的剪切板的库,是真的很繁琐,又是创建对象,又是绑定DOM,头都要炸了,就个简单的复制功能,第三方库换来换去地测试&hellip;&hellip;</p> +<p>后来看到了vueuse可以直接用,突然觉得,哇!真棒!</p> +<p>直到有一天,搜到了Clipboard api&hellip;&hellip;</p> +<h2 id="原生支持">原生支持</h2> +<p><em><strong>官方文档</strong></em>:https://developer.mozilla.org/en-US/docs/Web/API/Clipboard</p> +<p>不管是读,还是写,统统搞定!而且都还是异步方法。</p> +<p>比如复制文本到剪切板:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">writeText</span><span class="p">(</span><span class="s2">&#34;&lt;empty clipboard&gt;&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>复制canvas到剪切板:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">copyCanvasContentsToClipboard</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">onDone</span><span class="p">,</span> <span class="nx">onError</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toBlob</span><span class="p">((</span><span class="nx">blob</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">[</span><span class="k">new</span> <span class="nx">ClipboardItem</span><span class="p">({</span> <span class="p">[</span><span class="nx">blob</span><span class="p">.</span><span class="nx">type</span><span class="p">]</span><span class="o">:</span> <span class="nx">blob</span> <span class="p">})];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">data</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onDone</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onError</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>读取剪切板的文本:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">txt</span> <span class="o">=</span> <span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">readText</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + wow.js和animate css在vue3中的应用 + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + Sat, 28 Jan 2023 16:09:44 +0000 + + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + <h2 id="环境">环境</h2> +<ul> +<li>vue 3.2</li> +<li>typescript 4.7.4</li> +<li>wow.js 1.2.2</li> +<li>animate.css 4.1.1</li> +</ul> +<h2 id="animatecss">animate.css</h2> +<h3 id="下载">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add animate.css -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;animate.css&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<p>需要注意的是,animate css在4.0之后使用<code>animate__</code>前缀</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="动画延迟">动画延迟</h3> +<h4 id="官方方法">官方方法</h4> +<p>官方给出的动画延迟是<code>animate__delay-2s</code>、<code>animate__delay-3s</code> &hellip;&hellip;</p> +<p>直接在class中添加即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animate__delay-2s&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="自定义延迟">自定义延迟</h4> +<p>特殊场景需要使用不同于官方的延迟时间,因此可以自定义延迟时间,直接声明延迟的类,然后在class上加入即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-1</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">100</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">300</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-3</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">500</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-4</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">700</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-5</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">900</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-2&#34;</span><span class="p">&gt;</span>Another animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="wowjs">wow.js</h2> +<h3 id="下载-1">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add wow.js -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">WOW</span> <span class="nx">from</span> <span class="s1">&#39;wow.js&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">WOW</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">boxClass</span><span class="o">:</span> <span class="s1">&#39;wow&#39;</span><span class="p">,</span> <span class="c1">// 类名,在用户滚动时显示隐藏的框。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">animateClass</span><span class="o">:</span> <span class="s1">&#39;animate__animated&#39;</span><span class="p">,</span> <span class="c1">// 触发CSS动画的类名称 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">offset</span><span class="o">:</span> <span class="mi">300</span><span class="p">,</span> <span class="c1">// 定义浏览器视口底部与隐藏框顶部之间的距离。当用户滚动并到达此距离时,隐藏的框会显示出来。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mobile</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在移动设备上打开/关闭WOW.js。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">live</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在页面上同时检查新的WOW元素。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}).</span><span class="nx">init</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用-1">使用</h3> +<p>使用<code>wow</code>直接替代<code>animate__animated</code>即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;wow animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>由于写一个页面需要使用到wow,好多年没用过了,查了一下文档超多版本教程,使用起来各种不成功,难受&hellip;暂时也没找到可替代的方案&hellip;</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/graingert/wow" target="_blank" rel="noopener" + >wow</a></li> +<li><a class="link" href="https://animate.style/" target="_blank" rel="noopener" + >animate css</a></li> +</ul> + + + + Promise inside request interceptor + https://blog.hunterji.com/p/promise-inside-request-interceptor/ + Fri, 09 Apr 2021 11:06:25 +0000 + + https://blog.hunterji.com/p/promise-inside-request-interceptor/ + <h2 id="问题">问题</h2> +<p>在使用axios的拦截器时候,需要在request中调用一个promise函数,因此需要等待其执行完成才能去进行下一步。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getToken</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(...)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Request interceptors +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">service</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">config</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">getToken</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getToken</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(...)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// Request interceptors +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">service</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="kr">async</span> <span class="nx">config</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">awit</span> <span class="nx">getToken</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + 基于python3和js的前后端aes加解密 + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + Fri, 09 Oct 2020 21:12:50 +0000 + + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + <h2 id="简述">简述</h2> +<p>在特定敏感数据的场景需要加密,一开始采用<code>rsa</code>加密,但是<code>rsa</code>加密对性能要求较高,在解密时候对于数据量限制较大,导致加密传输的数据量上限较低。而采用<code>Base64</code>虽然简单明了但是解密过于简单。因此采用折中的对称加密<code>aes</code>。</p> +<p>而<code>aes</code>加密需要前后端加密类型相同,因此此处采用<code>CTR</code>,其对加密文本没有长度限制。</p> +<h2 id="前端实现">前端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端实现">后端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="示例代码">示例代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64encode</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Util</span> <span class="kn">import</span> <span class="n">Counter</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">codecs</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">hashlib</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">convert_to_md5</span><span class="p">(</span><span class="n">info</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> md5加密 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param info: 需要加密的内容 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: md5加密密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">md5</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">info</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">md5</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesCreateKey</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成aes加密的key,key的长度必须16位 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回key的base64密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">en_key</span> <span class="o">=</span> <span class="n">convert_to_md5</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">100000</span><span class="p">,</span> <span class="mi">999999</span><span class="p">)))[</span><span class="mi">8</span><span class="p">:</span><span class="o">-</span><span class="mi">8</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">b64encode</span><span class="p">(</span><span class="n">en_key</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto" target="_blank" rel="noopener" + >https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto</a></li> +</ul> + + + + + diff --git a/tags/javascript/page/1/index.html b/tags/javascript/page/1/index.html new file mode 100644 index 0000000..b416319 --- /dev/null +++ b/tags/javascript/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/javascript/ + + + + + + diff --git a/tags/nginx/index.html b/tags/nginx/index.html new file mode 100644 index 0000000..cbad6ac --- /dev/null +++ b/tags/nginx/index.html @@ -0,0 +1,537 @@ + + + + +Tag: Nginx - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

Nginx

+ +
+
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git a/tags/nginx/index.xml b/tags/nginx/index.xml new file mode 100644 index 0000000..b897f7c --- /dev/null +++ b/tags/nginx/index.xml @@ -0,0 +1,40 @@ + + + + Nginx on 开发者小橙 + https://blog.hunterji.com/tags/nginx/ + Recent content in Nginx on 开发者小橙 + Hugo -- gohugo.io + en-us + Sat, 15 Aug 2020 22:07:58 +0000 + nginx报错an upstream response is buffered to a temporary file + https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/ + Sat, 15 Aug 2020 22:07:58 +0000 + + https://blog.hunterji.com/p/nginx%E6%8A%A5%E9%94%99an-upstream-response-is-buffered-to-a-temporary-file/ + <h2 id="报错">报错</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">an upstream response is buffered to a temporary file +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p><code>Nginx</code>配置加上如下配置</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">proxy_max_temp_file_size 0<span class="p">;</span> +</span></span><span class="line"><span class="cl">client_max_body_size 50m<span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/nginx/page/1/index.html b/tags/nginx/page/1/index.html new file mode 100644 index 0000000..5f15a33 --- /dev/null +++ b/tags/nginx/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/nginx/ + + + + + + diff --git a/tags/node/index.html b/tags/node/index.html new file mode 100644 index 0000000..70d9511 --- /dev/null +++ b/tags/node/index.html @@ -0,0 +1,537 @@ + + + + +Tag: Node - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

Node

+ +
+
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git a/tags/node/index.xml b/tags/node/index.xml new file mode 100644 index 0000000..c9a16d0 --- /dev/null +++ b/tags/node/index.xml @@ -0,0 +1,66 @@ + + + + Node on 开发者小橙 + https://blog.hunterji.com/tags/node/ + Recent content in Node on 开发者小橙 + Hugo -- gohugo.io + en-us + Thu, 29 Jul 2021 13:49:13 +0000 + node spawn在windows下不生效问题记录 + https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + Thu, 29 Jul 2021 13:49:13 +0000 + + https://blog.hunterji.com/p/node-spawn%E5%9C%A8windows%E4%B8%8B%E4%B8%8D%E7%94%9F%E6%95%88%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + <h2 id="问题描述">问题描述</h2> +<p>使用<code>electron</code>开发的windows桌面应用程序,在调用目标文件夹底下的exe执行文件时,开发机子上没有问题,但是其他机子使用时一直调用失败,也抓取不到日志。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">spawn</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">remote</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">getAppPath</span><span class="p">(),</span> <span class="s2">&#34;../target.exe&#34;</span><span class="p">),</span> <span class="p">[],</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">shell</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">detached</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">windowsHide</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="原因">原因</h2> +<p><strong>路径存在空格。</strong></p> +<p>也是经过各种原因排查,然后一次偶然的成功才注意到了路径问题,排查之后发现确实是这问题&hellip;&hellip;</p> +<h2 id="解决">解决</h2> +<p><code>spawn</code>按照如上我的代码一定条件下可以运行,其有一个参数<code>cwd</code>,用来表明运行目录。<code>spawn</code>第一个参数必须是命令的名字,不能是路径。</p> +<p>所以如上代码改成这样:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">spawn</span><span class="p">(</span><span class="s2">&#34;target.exe&#34;</span><span class="p">,</span> <span class="p">[],</span> <span class="p">{</span> <span class="c1">// 此处直接写目标exe文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">cwd</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">remote</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">getAppPath</span><span class="p">(),</span> <span class="s2">&#34;../&#34;</span><span class="p">),</span> <span class="c1">// 注意这里,使用了cwd参数来写运行目录 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">shell</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">detached</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">windowsHide</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">});</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/21356372/node-child-process-spawn-not-working-with-spaces-in-path-on-windows" target="_blank" rel="noopener" + >node child_process.spawn not working with spaces in path on windows</a></li> +</ul> + + + + + diff --git a/tags/node/page/1/index.html b/tags/node/page/1/index.html new file mode 100644 index 0000000..480ea90 --- /dev/null +++ b/tags/node/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/node/ + + + + + + diff --git a/tags/objectstorage/index.html b/tags/objectstorage/index.html new file mode 100644 index 0000000..51b83b6 --- /dev/null +++ b/tags/objectstorage/index.html @@ -0,0 +1,548 @@ + + + + +Tag: ObjectStorage - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

2 pages

+

ObjectStorage

+ +
+
+
+ +
+ + + + + +
+ + + +
+
+ + + + + diff --git a/tags/objectstorage/index.xml b/tags/objectstorage/index.xml new file mode 100644 index 0000000..74ae104 --- /dev/null +++ b/tags/objectstorage/index.xml @@ -0,0 +1,217 @@ + + + + ObjectStorage on 开发者小橙 + https://blog.hunterji.com/tags/objectstorage/ + Recent content in ObjectStorage on 开发者小橙 + Hugo -- gohugo.io + en-us + Wed, 29 Sep 2021 09:22:22 +0000 + OSS花式解锁下载文件新姿势,你学废了吗? + https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/ + Wed, 29 Sep 2021 09:22:22 +0000 + + https://blog.hunterji.com/p/oss%E8%8A%B1%E5%BC%8F%E8%A7%A3%E9%94%81%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%BD%A0%E5%AD%A6%E5%BA%9F%E4%BA%86%E5%90%97/ + <h2 id="简介">简介</h2> +<p>在上一篇文章——<a class="link" href="https://mp.weixin.qq.com/s/qpSWVymlWtwlb5WUd_1M7Q" target="_blank" rel="noopener" + >前端文件花式直传OSS!后端:那我走?</a>中聊了下文件上传的几种方案,这里我们再来聊一下文件下载的花式姿势。</p> +<h2 id="精简版">精简版</h2> +<p>最常见的方式,莫过于后端存储文件在服务器上,然后通过后端接口传给前端,如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwoojpvg6j60lq068jrg02.jpg" + + + + loading="lazy" + + alt="最简版" + + +></p> +<p>该方案对于小规模、成本较低的项目非常适用,开发也较为便捷。</p> +<p>而对于有性能要求的项目,可以通过砸钱加机器、分片下载等方案提升项目性能。如果可以的话,请砸钱加机器吧!</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwq8edesqj60hq0bc75c02.jpg" alt="截屏2021-09-28 下午9.58.10" style="zoom:33%;" /> +<h2 id="中庸版">中庸版</h2> +<p>相较于上一个方案,可以砸丢丢钱整个OSS,将文件存储在OSS上,毕竟OSS上行流量不收费,如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwpuow9hnj60km0dc74n02.jpg" + + + + loading="lazy" + + alt="中庸版直传" + + +></p> +<p>那么问题来了,OSS的下行流量不是收费的吗?!</p> +<p>OK,偷偷告诉各位一个省钱小妙招/狗头,OSS内网的下行流量是不收费的!因此,可以通过后端请求OSS,获取到文件/字符串后,将其以文件流/base64数据的方式返回给前端。这样就避免了下行流量的费用。如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwp05f04jj60r3068glu02.jpg" + + + + loading="lazy" + + alt="中庸版" + + +></p> +<p>不过问题又来了,这样就还是占用了后端服务器的资源,依然会是性能的一个瓶颈。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwq8of8qcj6076075dft02.jpg" alt="截屏2021-09-28 下午10.12.06" style="zoom:50%;" /> +<h2 id="性能版">性能版</h2> +<p>基于上一个方案,可以再升级。砸丢丢钱,拉上CDN这老哥,利用CDN流量代替OSS的下行流量,既能让前端直接请求OSS资源,不占用服务器资源,也降低了成本。如图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guwplrnpu2j60r20dc3z102.jpg" + + + + loading="lazy" + + alt="性能版" + + +></p> +<p>在优先性能的情况下,该方案是较优的。</p> +<p>要说缺点的话,就是CDN配置吧,需要买域名和SSL证书等,不过一次购买,后续使用体验会非常棒。CDN除了可以代替OSS的下行流量外,其优点不要太多,比如说CDN可以文件缓存、可以调度至加速节点等。</p> +<p>涉及到OSS的私有Bucket的话,只需要使用CDN的访问控制即可。其也只需要通过后端实现加密,生成文件访问URL给前端直接访问。</p> +<p>或许你会存在疑问,看上去挺麻烦的啊!但是看看CDN的价格,你肯定会有不一样的想法的。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gux8jydxbdj608v09agly02.jpg" alt="截屏2021-09-28 下午10.37.14" style="zoom:50%;" /> + + + 前端文件花式直传OSS!后端:那我走? + https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/ + Tue, 28 Sep 2021 09:53:18 +0000 + + https://blog.hunterji.com/p/%E5%89%8D%E7%AB%AF%E6%96%87%E4%BB%B6%E8%8A%B1%E5%BC%8F%E7%9B%B4%E4%BC%A0oss%E5%90%8E%E7%AB%AF%E9%82%A3%E6%88%91%E8%B5%B0/ + <h2 id="简介">简介</h2> +<p>前端还在传文件给后端吗?你们的服务器扛得住吗?什么&hellip;&hellip;老板砸钱加机器?!告辞!/狗头</p> +<p>前后端文件传输涉及数据较大,往往会成为很多项目的性能瓶颈。常见的传输方式也有不少,相对来说,OSS直传能够减轻很大压力。</p> +<p>本文我们来列举下常见的和oss直传的几种传输方式,并列举其优劣。</p> +<h2 id="常见方式">常见方式</h2> +<h3 id="表单上传">表单上传</h3> +<p>表单上传文件是最常见的方式,前后端开发小伙伴都很轻松,前端哐哐传,后端哐哐收就成了。其过程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guw3hw7k3uj60ix06q74b02.jpg" + + + + loading="lazy" + + alt="form上传" + + +></p> +<p><strong>优势:</strong></p> +<ul> +<li>简单方便,开发量小</li> +<li>前后端原生支持,无需额外第三方库支持</li> +</ul> +<h3 id="base64上传">Base64上传</h3> +<p>Base64方式上传文件,多常见于小文件,如小图片等,前后端都可直接使用<code>String</code>类型发送和接收。不过在前端,需要将文件转成base64数据,不仅会增加些性能消耗,还会增加传输数据的体积。而对于后端,如果并不是想直接存储base64数据,也还需要将其转成文件再存储,也会增加后端的性能消耗。</p> +<p>该上传方式可适用于<code>Resetful Api</code>,也可适用于文件的加密、回调接口携带文件等等。其过程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvkwpx4d9j60ix06q74d02.jpg" + + + + loading="lazy" + + alt="base64上传" + + +></p> +<p><strong>优势:</strong></p> +<ul> +<li>适用于<code>Resetful Api</code>,可用于加密、回调等场景,较为灵活</li> +</ul> +<p><strong>劣势:</strong></p> +<ul> +<li>前后端文件与base64数据转换需要消耗性能</li> +<li>只适用于小文件</li> +</ul> +<h2 id="oss直传">OSS直传</h2> +<p>此处的OSS直传方案都是使用的阿里云的OSS产品,以下将介绍三个方案,可适用于不同的场景。</p> +<h3 id="browserjs-sdk上传">Browser.js SDK上传</h3> +<p>该方案可在前端直接通过<code>browser.js</code>上传文件到OSS,可分成三步:</p> +<ol> +<li>前端使用SDK直传OSS</li> +<li>前端上传完成后请求后端,通知上传完成</li> +<li>后端检测OSS上该文件是否存在(可选)</li> +</ol> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvlvyap7zj60lc0crjru02.jpg" + + + + loading="lazy" + + alt="browser直传" + + +></p> +<p><code>Browser.js</code>的方式需要前端安装阿里云的库<code>ali-oss</code>,然后在前端调用。直传还需要OSS账户的Key和Secret,因此为了安全考虑,需要建立RAM账户,然后前端向后端先请求一个<code>STS</code>临时访问凭证来完成直传,其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvlvuqm2bj60lc0cr0tb02.jpg" + + + + loading="lazy" + + alt="browser直传2" + + +></p> +<h3 id="javascript客户端签名直传">Javascript客户端签名直传</h3> +<p>Javascript客户端签名直传,需要先从后端获取临时签名,其流程与上一步的<code>browser.js</code>方案大致相同,不同点在于:</p> +<ul> +<li>无需第三方库支持,直接表单上传</li> +<li>原生支持后端上传回调(下一步骤讲述)</li> +</ul> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvm1snim8j60lc0crgm202.jpg" + + + + loading="lazy" + + alt="javascript签名直传" + + +></p> +<p>不过该方案做下来,我感觉最大的问题是权限配置有点麻烦&hellip;&hellip;/泪眼</p> +<h3 id="服务端签名直传并设置上传回调">服务端签名直传并设置上传回调</h3> +<p>该方案其实是上面方案——Javascript客户端签名直传的升级版本,其加上了后端的上传回调功能。不错呦~</p> +<p>前端需要改动的很少,只需要在请求参数中加上<code>callback</code>参数即可,该参数为后端加密,在签名请求的响应中一起返回回来,内加密了后端回调接口。在前端直传完成后,后端回调接口将会接收到相关文件参数,包括文件路径、大小、类型等。最后OSS会将回调接口response转发给前端,响应直传OSS的请求。</p> +<p>其流程如下图所示。</p> +<p><img src="https://tva1.sinaimg.cn/large/008i3skNgy1guvmceaql3j60lc0craak02.jpg" + + + + loading="lazy" + + alt="后端签名直传且回调" + + +></p> +<h2 id="对比">对比</h2> +<p>传统方式相比直传OSS,相对来说有三个缺点:</p> +<ul> +<li>上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。</li> +<li>扩展性差:如果后续用户多了,应用服务器会成为瓶颈。</li> +<li>费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。</li> +</ul> +<p>当然,对于规模较小、成本较低的项目来说,常见的上传方式还是适合的,毕竟没有最好的,只有最适合的。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/112718.html?spm=5176.22414175.sslink.2.3fc92aca2CzF4n" target="_blank" rel="noopener" + >Web端上传介绍</a></p> +</li> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/31925.html" target="_blank" rel="noopener" + >JavaScript客户端签名直传</a></p> +</li> +<li> +<p><a class="link" href="https://help.aliyun.com/document_detail/267439.html?spm=a2c4g.11186623.0.0.13415d3fbA6bxA" target="_blank" rel="noopener" + >服务端签名直传并设置上传回调</a></p> +</li> +</ul> + + + + + diff --git a/tags/objectstorage/page/1/index.html b/tags/objectstorage/page/1/index.html new file mode 100644 index 0000000..9d0cc6c --- /dev/null +++ b/tags/objectstorage/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/objectstorage/ + + + + + + diff --git a/tags/page/1/index.html b/tags/page/1/index.html new file mode 100644 index 0000000..fe3d70c --- /dev/null +++ b/tags/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/ + + + + + + diff --git a/tags/page/2/index.html b/tags/page/2/index.html new file mode 100644 index 0000000..e125be8 --- /dev/null +++ b/tags/page/2/index.html @@ -0,0 +1,608 @@ + + + + +Tags + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

18 pages

+

Tags

+ +
+
+
+ + +
+ + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/tags/python/index.html b/tags/python/index.html new file mode 100644 index 0000000..a2a1fe7 --- /dev/null +++ b/tags/python/index.html @@ -0,0 +1,647 @@ + + + + +Tag: Python - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

11 pages

+

Python

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/python/index.xml b/tags/python/index.xml new file mode 100644 index 0000000..47bdbd5 --- /dev/null +++ b/tags/python/index.xml @@ -0,0 +1,1593 @@ + + + + Python on 开发者小橙 + https://blog.hunterji.com/tags/python/ + Recent content in Python on 开发者小橙 + Hugo -- gohugo.io + en-us + Thu, 09 Jun 2022 22:57:54 +0000 + python3 socket tcp example + https://blog.hunterji.com/p/python3-socket-tcp-example/ + Thu, 09 Jun 2022 22:57:54 +0000 + + https://blog.hunterji.com/p/python3-socket-tcp-example/ + <h2 id="socket-tcp-server">socket tcp server</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_tcp_server</span><span class="p">(</span><span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s1">&#39;0.0.0.0&#39;</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">9000</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket tcp 服务端 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务器tcp server接收信息的端口, 默认9000 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param buffer_size: 套接字缓冲区大小, 默认1024 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;服务端开始运行...</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">client</span><span class="p">,</span> <span class="n">sender_info</span> <span class="o">=</span> <span class="n">tcp_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">receive_data</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;客户端地址: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sender_info</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;来自客户端的信息: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">receive_data</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1"># 返回消息</span> +</span></span><span class="line"><span class="cl"> <span class="n">client</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;response...&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_tcp_server</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="socket-tcp-client">socket tcp client</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_tcp_client_send_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket tcp 客户端发送消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param message: 消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务端的ip地址 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务端的端口号 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">family</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">response</span> <span class="o">=</span> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;response : </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">decode</span><span class="p">()))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">tcp_client_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_tcp_client_send_message</span><span class="p">(</span><span class="s1">&#39;hello,world!&#39;</span><span class="p">,</span> <span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">9000</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + python3 socket udp example + https://blog.hunterji.com/p/python3-socket-udp-example/ + Thu, 09 Jun 2022 22:57:54 +0000 + + https://blog.hunterji.com/p/python3-socket-udp-example/ + <h2 id="socket-udp-server">socket udp server</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_udp_server</span><span class="p">(</span><span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s1">&#39;0.0.0.0&#39;</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">9000</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket udp 服务端 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务器的地址, 默认为0.0.0.0, 表示允许所有 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务器udp server接收信息的端口, 默认9000 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param buffer_size: 套接字缓冲区大小, 默认1024 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_DGRAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;服务端开始运行...</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">receive_data</span><span class="p">,</span> <span class="n">sender_info</span> <span class="o">=</span> <span class="n">udp_socket</span><span class="o">.</span><span class="n">recvfrom</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;客户端地址: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sender_info</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;来自客户端的信息: </span><span class="si">{}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">receive_data</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_udp_server</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="socket-udp-client">socket udp client</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">socket_udp_client_send_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_ip</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">server_port</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> socket udp 客户端发送消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param message: 消息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_ip: 服务端的ip地址 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param server_port: 服务端的端口号 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: none +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_client_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">family</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">SOCK_DGRAM</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">udp_client_socket</span><span class="o">.</span><span class="n">sendto</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="p">(</span><span class="n">server_ip</span><span class="p">,</span> <span class="n">server_port</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">socket_udp_client_send_message</span><span class="p">(</span><span class="s1">&#39;hello,world!&#39;</span><span class="p">,</span> <span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">9000</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask设置全局错误捕获 + https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/ + Mon, 02 Nov 2020 20:51:02 +0000 + + https://blog.hunterji.com/p/flask%E8%AE%BE%E7%BD%AE%E5%85%A8%E5%B1%80%E9%94%99%E8%AF%AF%E6%8D%95%E8%8E%B7/ + <h2 id="前言">前言</h2> +<p>代码运行过程中,意外情况会导致<code>500</code>错误,对于使用者来说体验很不好,对于开发者来说也无法及时获取错误,需要去查看日志。</p> +<p>并且有的插件在某些报错情况下会返回一些敏感信息,非常危险。因此需要去捕获全局错误,通知开发者,自定义错误消息等。</p> +<h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">app</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">werkzeug.exceptions</span> <span class="kn">import</span> <span class="n">HTTPException</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="ne">Exception</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">all_exception_handler</span><span class="p">(</span><span class="n">e</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span> <span class="o">==</span> <span class="mi">404</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">40004</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;message&#39;</span><span class="p">:</span> <span class="s1">&#39;404&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">404</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1"># 通知开发者/写入日志</span> +</span></span><span class="line"><span class="cl"> <span class="n">handle</span><span class="p">(</span><span class="n">path</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">content</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">20001</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;message&#39;</span><span class="p">:</span> <span class="s1">&#39;Error&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + flask_restful限制request字段长度 + https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/ + Mon, 02 Nov 2020 20:24:35 +0000 + + https://blog.hunterji.com/p/flask_restful%E9%99%90%E5%88%B6request%E5%AD%97%E6%AE%B5%E9%95%BF%E5%BA%A6/ + <h2 id="前言">前言</h2> +<p>当前产品遇到一个报错,就是接口收到请求没有限制请求字段长度,导致字段长度超过数据库对应字段长度,直接报了500。因此也对此有些新的需求,需要在后端限制请求字段最大长度。</p> +<h2 id="环境">环境</h2> +<ul> +<li><strong>开发语言</strong>:Python 3.7</li> +<li><strong>后端框架</strong>:Flask 1.1.1</li> +<li><strong>插件</strong>:Flask-RESTful 0.3.8</li> +</ul> +<h2 id="实现">实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">field_max_limit</span><span class="p">(</span><span class="n">max_length</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field must be String.&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_length</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field cannot exceed </span><span class="si">%i</span><span class="s2"> characters.&#34;</span> <span class="o">%</span> <span class="n">max_length</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 解析请求参数时候验证长度</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="示例">示例</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_restful</span> <span class="kn">import</span> <span class="n">Api</span><span class="p">,</span> <span class="n">Resource</span><span class="p">,</span> <span class="n">reqparse</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">werkzeug.routing</span> <span class="kn">import</span> <span class="n">ValidationError</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">api</span> <span class="o">=</span> <span class="n">Api</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">field_max_limit</span><span class="p">(</span><span class="n">max_length</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field must be String.&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_length</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span> +</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;The field cannot exceed </span><span class="si">%i</span><span class="s2"> characters.&#34;</span> <span class="o">%</span> <span class="n">max_length</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">validate</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Login</span><span class="p">(</span><span class="n">Resource</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="n">field_max_limit</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;username&#39;</span><span class="p">:</span> <span class="n">args</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;password&#39;</span><span class="p">:</span> <span class="n">args</span><span class="o">.</span><span class="n">password</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;code&#39;</span><span class="p">:</span> <span class="mi">20000</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">api</span><span class="o">.</span><span class="n">add_resource</span><span class="p">(</span><span class="n">Login</span><span class="p">,</span> <span class="s1">&#39;/login&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + 基于python3和js的前后端aes加解密 + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + Fri, 09 Oct 2020 21:12:50 +0000 + + https://blog.hunterji.com/p/%E5%9F%BA%E4%BA%8Epython3%E5%92%8Cjs%E7%9A%84%E5%89%8D%E5%90%8E%E7%AB%AFaes%E5%8A%A0%E8%A7%A3%E5%AF%86/ + <h2 id="简述">简述</h2> +<p>在特定敏感数据的场景需要加密,一开始采用<code>rsa</code>加密,但是<code>rsa</code>加密对性能要求较高,在解密时候对于数据量限制较大,导致加密传输的数据量上限较低。而采用<code>Base64</code>虽然简单明了但是解密过于简单。因此采用折中的对称加密<code>aes</code>。</p> +<p>而<code>aes</code>加密需要前后端加密类型相同,因此此处采用<code>CTR</code>,其对加密文本没有长度限制。</p> +<h2 id="前端实现">前端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端实现">后端实现</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="示例代码">示例代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">aesEncrypted</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">iv</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">cipher</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">createCipheriv</span><span class="p">(</span><span class="s2">&#34;aes-128-ctr&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">iv</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">iv</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nx">cipher</span><span class="p">.</span><span class="kr">final</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64encode</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Util</span> <span class="kn">import</span> <span class="n">Counter</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">codecs</span> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">hashlib</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">convert_to_md5</span><span class="p">(</span><span class="n">info</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> md5加密 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param info: 需要加密的内容 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: md5加密密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">md5</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">md5</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">info</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">md5</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesCreateKey</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成aes加密的key,key的长度必须16位 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回key的base64密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">en_key</span> <span class="o">=</span> <span class="n">convert_to_md5</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">100000</span><span class="p">,</span> <span class="mi">999999</span><span class="p">)))[</span><span class="mi">8</span><span class="p">:</span><span class="o">-</span><span class="mi">8</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">b64encode</span><span class="p">(</span><span class="n">en_key</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">aesDecryption</span><span class="p">(</span><span class="n">key_</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">de_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> aes解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param key_: aes的key +</span></span></span><span class="line"><span class="cl"><span class="s2"> :param de_text: aes加密的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密的文本 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ct</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">de_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="s1">&#39;hex&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">ct</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">initial_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key_</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ct</span><span class="p">[</span><span class="mi">16</span><span class="p">:])</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto" target="_blank" rel="noopener" + >https://stackoverflow.com/questions/44996742/encrypt-with-node-js-aes-ctr-and-decrypt-with-pycrypto</a></li> +</ul> + + + + Flask_RESTful解析常见类型请求数据 + https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/ + Wed, 05 Aug 2020 15:49:56 +0000 + + https://blog.hunterji.com/p/flask_restful%E8%A7%A3%E6%9E%90%E5%B8%B8%E8%A7%81%E7%B1%BB%E5%9E%8B%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE/ + <h2 id="前言">前言</h2> +<p><code>Flask_RESTful</code>是一个Flask 扩展,它添加了快速构建 REST APIs 的支持。其请求解析接口是模仿 <code>argparse</code> 接口。它设计成提供简单并且统一的访问 Flask 中 <code>flask.request</code> 对象里的任何变量的入口。</p> +<br /> +<h2 id="常见类型解析">常见类型解析</h2> +<h3 id="基本参数">基本参数</h3> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="s2">&#34;heihei&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="必选参数">必选参数</h3> +<p>使用参数<code>required</code>。</p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="s2">&#34;heihei&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="列表string">列表[string]</h3> +<p>使用参数<code>action = 'append'</code></p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;handsome&#34;</span><span class="p">,</span> <span class="s2">&#34;cheerful&#34;</span><span class="p">,</span> <span class="s2">&#34;optimism&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">action</span> <span class="o">=</span> <span class="s1">&#39;append&#39;</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="列表dict">列表[dict]</h3> +<p>使用参数<code>action = 'append'</code></p> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;friends&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;tom&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;jerry&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">action</span> <span class="o">=</span> <span class="s1">&#39;append&#39;</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><br /> +<h3 id="json">JSON</h3> +<ul> +<li>请求</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;info&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;character&#34;</span><span class="p">:</span> <span class="s2">&#34;optimism&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;age&#34;</span><span class="p">:</span> <span class="mi">20</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>解析</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parse</span> <span class="o">=</span> <span class="n">reqparse</span><span class="o">.</span><span class="n">RequestParser</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">str</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">parse</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="nb">type</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">args</span> <span class="o">=</span> <span class="n">parse</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + flask_sqlalchemy一对一、一对多、多对一、多对多 + https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/ + Tue, 23 Jun 2020 13:46:21 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E4%B8%80%E5%AF%B9%E4%B8%80%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%A4%9A%E5%AF%B9%E4%B8%80%E5%A4%9A%E5%AF%B9%E5%A4%9A/ + <h2 id="一对多">一对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Country</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">capital</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Capital&#39;</span><span class="p">,</span> <span class="n">uselist</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Capital</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">country_id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s1">&#39;country.id&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">country</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Country&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="一对多-1">一对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">phone</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Article</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">title</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="n">index</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">body</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Text</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多对一">多对一</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Citizen</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">city_id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s1">&#39;city.id&#39;</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">city</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;City&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">City</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多对多">多对多</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">association_table</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Table</span><span class="p">(</span><span class="s1">&#39;association&#39;</span><span class="p">,</span><span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s1">&#39;student_id&#39;</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">Foreign</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Student</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">grade</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="n">teachers</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">relationship</span><span class="p">(</span><span class="s1">&#39;Teacher&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">secondary</span><span class="o">=</span><span class="n">association_table</span><span class="p">,</span> <span class="n">back_populates</span><span class="o">=</span><span class="s1">&#39;students&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Teacher</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">70</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">office</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue+flask前后端信息传输非对称加密 + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + Fri, 22 May 2020 15:11:05 +0000 + + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + <h2 id="介绍">介绍</h2> +<p>为了前后端传输数据的安全性,需要对数据进行加密。因此选定使用非对称加密,此处为<code>RSA</code>。</p> +<p>在传输数据前,后端生成公钥和私钥,将公钥给前端,前端加密之后,将密文传给后端,后端使用私钥解密即可得到原始的数据。</p> +<h2 id="环境">环境</h2> +<ul> +<li><strong>前端</strong>使用<code>jsencrypt</code>加密</li> +<li><strong>后端</strong>使用<code>Crypto</code>生成密钥和解密</li> +</ul> +<h2 id="后端生成密钥">后端生成密钥</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="前端用公钥加密">前端用公钥加密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端使用私钥解密">后端使用私钥解密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>这里使用<code>base64</code>先解密一遍是必要的,否则报错<code>ValueError: Ciphertext with incorrect length.</code></li> +</ul> +<h2 id="完整代码">完整代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_encrypt</span><span class="p">(</span><span class="n">public_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa加密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params publick_key: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 需要加密的信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">public_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">encrypt</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="分段加密">分段加密</h2> +<p><code>RSA</code>加密信息最长为<code>128</code>位,过长则会报错,因此,对于过长的信息需要分段加密,后端也要分段解密后拼装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">en_str</span><span class="p">(</span><span class="nx">target_str</span><span class="p">,</span> <span class="nx">pubkey</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">en_array</span> <span class="o">=</span> <span class="p">[];</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">n</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">// 每段信息的长度 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">l</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">l</span> <span class="o">/</span> <span class="nx">n</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">message</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">n</span> <span class="o">*</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">n</span> <span class="o">*</span> <span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="nx">en_array</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">en_array</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask_sqlalchemy的增删改查 + https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/ + Fri, 20 Mar 2020 12:19:52 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/ + <h2 id="增">增</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;article1&#39;</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s1">&#39;heihei&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>若要在新增之后获取数据库中新增数据的信息,如<code>id</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;article1&#39;</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s1">&#39;heihei&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span> <span class="c1"># 添加这一条,用于预提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 输出新增数据的信息</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="删">删</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1">##把需要删除的数据查找出来</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">content</span> <span class="o">=</span> <span class="s1">&#39;heihei&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##把这条数据删除掉</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">article</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="改">改</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1">##先把你要更改的数据查找出来</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s1">&#39;article1&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##把这条数据需要修改的地方进行修改</span> +</span></span><span class="line"><span class="cl"><span class="n">article</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article2&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">##提交</span> +</span></span><span class="line"><span class="cl"><span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="查">查</h3> +<h3 id="查询单个">查询单个</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> <span class="c1"># 或者</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article1</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">article1</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="查询所有">查询所有</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">article1</span><span class="p">:</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="倒叙">倒叙</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">desc</span><span class="p">())</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="限制数量">限制数量</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">article1</span> <span class="o">=</span> <span class="n">Article</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;article&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">limit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Flask_sqlalchemy报错MySQL server has gone away解决 + https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/ + Thu, 12 Mar 2020 21:44:36 +0000 + + https://blog.hunterji.com/p/flask_sqlalchemy%E6%8A%A5%E9%94%99mysql-server-has-gone-away%E8%A7%A3%E5%86%B3/ + <h3 id="报错">报错</h3> +<p>使用<code>flask_sqlalchemy</code>,服务端出现500错误,日志显示报错如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">MySQL server has gone away +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="解决">解决</h3> +<p>添加配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">SQLALCHEMY_POOL_RECYCLE</span> <span class="o">=</span> <span class="mi">280</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>该配置作用是设置多少秒后回收连接,如果不提供值,默认为 2 小时。此处将其设置为280秒。</p> +<p>该配置原文解释:</p> +<blockquote> +<p>Number of seconds after which a connection is automatically recycled. This is required for MySQL, which removes connections after 8 hours idle by default. Note that Flask-SQLAlchemy automatically sets this to 2 hours if MySQL is used.</p> +</blockquote> + + + + Flask_Restful视图函数模块化 + https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/ + Sun, 23 Feb 2020 14:55:36 +0000 + + https://blog.hunterji.com/p/flask_restful%E8%A7%86%E5%9B%BE%E5%87%BD%E6%95%B0%E6%A8%A1%E5%9D%97%E5%8C%96/ + <p>Restful作为目前流行的api设计规范,在flask上也有较好的实践,即为flask_restful。我们在使用flask_restful的时候,当代码量达到一定程度,需要将视图函数模块化。然而在Flask之前一直使用Blueprint模块化,那么flask_restful如何模块化呢?底下就来瞅瞅!</p> +<h2 id="当前架构">当前架构</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views.py +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li><code>__init__.py</code>:将该文件夹标示为一个模块</li> +<li><code>commands.py</code>:额外命令,如初始化数据库</li> +<li><code>config.py</code>:配置文件</li> +<li><code>views.py</code>:视图函数</li> +</ul> +<p>此为一个简单的flask项目架构,我们可以将flask中的各个功能拆分出来,分给不同文件,最后在<code>__init__.py</code>导入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_cors</span> <span class="kn">import</span> <span class="n">CORS</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_restful</span> <span class="kn">import</span> <span class="n">Api</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="s1">&#39;server&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="s1">&#39;config.py&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">CORS</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">api</span> <span class="o">=</span> <span class="n">Api</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span><span class="p">,</span> <span class="n">views</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="初步模块化">初步模块化</h2> +<p>现在将视图函数改为一个文件夹<code>views</code>,然后在该文件夹下放入拆分后的文件。这里以登录和获取用户信息接口为例,架构就变成了下面这样。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views +</span></span><span class="line"><span class="cl"> ├── __init__.py +</span></span><span class="line"><span class="cl"> ├── login.py +</span></span><span class="line"><span class="cl"> └── info.py +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处的<code>__init__.py</code>文件功能如上,是将文件夹<code>views</code>标示为模块,但是此处的<code>__init__py</code>是个空白文件,但是<code>server</code>文件夹中的<code>__init__.py</code>的引入需要改变。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server.views</span> <span class="kn">import</span> <span class="n">login</span><span class="p">,</span> <span class="n">info</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="最终模块化">最终模块化</h2> +<p>此时还不够,例如登录和获取用户信息接口是<code>User</code>模块下的,而获取文章标题接口是<code>Article</code>模块的下的,因此我们继续分。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server/ +</span></span><span class="line"><span class="cl">├── __init__.py +</span></span><span class="line"><span class="cl">├── commands.py +</span></span><span class="line"><span class="cl">├── config.py +</span></span><span class="line"><span class="cl">└── views +</span></span><span class="line"><span class="cl"> ├── Aricle +</span></span><span class="line"><span class="cl"> │   ├── __init__.py +</span></span><span class="line"><span class="cl"> │   └── title.py +</span></span><span class="line"><span class="cl"> ├── User +</span></span><span class="line"><span class="cl"> │   ├── __init__.py +</span></span><span class="line"><span class="cl"> │   ├── info.py +</span></span><span class="line"><span class="cl"> │   └── login.py +</span></span><span class="line"><span class="cl"> └── __init__.py +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>server</code>文件夹中的<code>__init__.py</code>的引入继续改变。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">...</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">commands</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">server.views.User</span> <span class="kn">import</span> <span class="n">login</span><span class="p">,</span> <span class="n">info</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">srever.views.Article</span> <span class="kn">import</span> <span class="n">title</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>至此,关于<code>flask_restful</code>的视图函数模块化结束,若有后续改进会继续分享,若大家有更好的方式请务必分享一下。</p> +<p>当时为了解决这个问题查了很久,但是要么是介绍blueprint的,要么就是flask_restful插件入门,简直刺激&hellip;</p> + + + + + diff --git a/tags/python/page/1/index.html b/tags/python/page/1/index.html new file mode 100644 index 0000000..f121519 --- /dev/null +++ b/tags/python/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/python/ + + + + + + diff --git a/tags/rust/index.html b/tags/rust/index.html new file mode 100644 index 0000000..eaf0cca --- /dev/null +++ b/tags/rust/index.html @@ -0,0 +1,606 @@ + + + + +Tag: Rust - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

4 pages

+

Rust

+ +
+
+
+ +
+ + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/rust/index.xml b/tags/rust/index.xml new file mode 100644 index 0000000..55f2677 --- /dev/null +++ b/tags/rust/index.xml @@ -0,0 +1,1947 @@ + + + + Rust on 开发者小橙 + https://blog.hunterji.com/tags/rust/ + Recent content in Rust on 开发者小橙 + Hugo -- gohugo.io + en-us + Thu, 26 Oct 2023 23:51:13 +0000 + 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + Thu, 26 Oct 2023 23:51:13 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(三)——Rust与JS交互》</a>中,讲述了Rust与JS的交互,包括Rust与JS的函数相互调用,比较炸裂的功能就是使用JS调用Rust的struct,JS本身连struct都没有,居然可以调用Rust的struct,这对于Rust开发者的开发体验而言,是真的很棒!</p> +<p>基于前面的系列文章,已经足以使用Rust开发一个完整的功能了。</p> +<p>但是,在前端引入wasm文件时,还是可能存在一些问题,比如wasm文件较大,导致网页访问时间较长,用户体验较差。本篇文章将会通过多种途径来减少Rust编译wasm文件的体积,以减少前端加载wasm文件的时间。</p> +<p>曾经一段时间,我一直用<a class="link" href="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/" target="_blank" rel="noopener" + >Go开发WebAssembly</a>,其编译后的wasm文件体积还是较大的,为了减少wasm文件的体积真是煞费苦心,1xMB大小的wasm文件真的是太痛了&hellip;&hellip;但是体积优化往往都会指向一条“充满魅惑的不归路”——换成Rust开发😆!如果你已经在用Rust开发WebAssembly了,那么恭喜你,对Go而言,在wasm体积上,你已经赢在起跑线上了。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="查看体积">查看体积</h2> +<p>在真正去减少体积前,我们需要来先看一下,当前情况下体积是多少,方便后续对比前后体积。</p> +<p>查看体积的方式有多种,这里推荐几个,(Linux和MacOS)使用其一即可。</p> +<h3 id="ls">ls</h3> +<p>可以使用<code>ls -l</code>或者<code>ll</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ll pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">-rw-r--r-- <span class="m">1</span> kuari staff 23K Jul <span class="m">20</span> 21:52 pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="stat">stat</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ stat pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"><span class="m">16777222</span> <span class="m">142141572</span> -rw-r--r-- <span class="m">1</span> kuari staff <span class="m">0</span> <span class="m">23347</span> <span class="s2">&#34;Jul 20 21:52:53 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="m">4096</span> <span class="m">48</span> <span class="m">0</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="wc">wc</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23347</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>以<code>wc</code>为例,当前该wasm文件体积为<strong>23347b</strong>。</p> +<h2 id="代码层面">代码层面</h2> +<blockquote> +<p>Link-Time Optimization (LTO) 是指在程序链接时进行的一种过程间优化(interprocedural optimization)。它允许编译器在链接阶段对多个编译单元进行优化,从而提高程序的性能、可靠性和安全性。</p> +</blockquote> +<p>从代码层面优化,主要是利用LTO(Link-Time Optimization)。</p> +<h3 id="代码内">代码内</h3> +<p>在<code>Cargo.toml</code>中开启LTO:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>开启LTO虽然能够减少编译后的体积,但是也会增加编译时间。</p> +<p>LTO开启后,默认是在减少一定程度的编译体积的情况下,要确保编译的时间。如果你的需求就是更小的体积,而不是较短的时间,那么,可以通过手动指定编译等级来让LTO作出改变。</p> +<p>在代码内可以使用如下等级:</p> +<ul> +<li><code>s</code>:默认的 LTO 等级。它会进行最基本的 LTO 优化,包括内联函数、函数重写、数据重排等</li> +<li><code>z</code>:最高级的 LTO 等级。它会进行更复杂的 LTO 优化,包括死代码消除、内存分配优化、安全性优化等</li> +</ul> +<p>那么可以在<code>Cargo.toml</code>中这么配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="nx">opt-level</span> <span class="p">=</span> <span class="s1">&#39;z&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>原始的文件体积是23347b,现在编译后看一下体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19879</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>很明显是减少体积!但是,使用<code>z</code>等级并不代表一定每次体积都会比<code>s</code>小的,有时候<code>s</code>也会比<code>z</code>小,这需要视代码情况而定。</p> +<h3 id="代码外">代码外</h3> +<p>在代码外,可以使用<a class="link" href="https://github.com/WebAssembly/binaryen" target="_blank" rel="noopener" + >wasm-opt</a>来进行优化,其可以对 WebAssembly 模块进行多方面的优化,当然本篇文章中重点在体积方面(挖个坑,后面再详聊/狗头)。并且wasm-opt可以对所有符合WebAssembly规范的wasm文件进行优化,所以,就算你不是Rust写的,那也可以用其进行优化。(想想我曾经Go写的wasm,也是有多一个法子可以优化一把了&hellip;&hellip;)</p> +<p>首先,来看一下wasm-opt的基本优化参数:</p> +<ul> +<li><strong>-o</strong>:指定优化后的模块输出文件</li> +<li><strong>-O</strong>:启用默认优化,等同于<code>-Os</code>参数</li> +<li><strong>-O0</strong>:不进行任何优化</li> +<li><strong>-O1</strong>:进行一些基本的优化,例如内联函数优化和死代码消除优化</li> +<li><strong>-O2</strong>:进行更为彻底的优化,例如函数重写、数据重排、内存分配优化等</li> +<li><strong>-O3</strong>:进行最为彻底的优化,包括一些可能影响程序功能的优化</li> +<li><strong>-O4</strong>:与 <code>-O3</code> 相同,但会启用更为激进的优化</li> +<li><strong>-Os</strong>:优化目标是减小代码大小,会进行一些可能影响性能的优化</li> +<li><strong>-Oz</strong>:与 <code>-Os</code> 相同,但会启用更为激进的优化</li> +</ul> +<p>基于本篇文章主题,此处将使用<code>-Os</code>和<code>-Oz</code>两种参数,其于上述&quot;代码内&quot;的等级是对应的。</p> +<p>此处以原始wasm文件,以<code>-Oz</code>参数来执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23194</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>再以上述开启“代码内“LTO编译后的wasm文件,以wasm-opt执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19871</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>总体而言,wasm文件的体积越来越小。只是当前我这里的案例,是沿用系列文章内容的代码,没有什么实际性复杂代码,再者本身体积已经很小了,所以不会特别有效果。</p> +<h2 id="网络层面">网络层面</h2> +<p>网络层面的话,就是众所周知的在网络传输时,客户端和服务端约定相同的压缩算法,然后服务端给出时进行压缩,客户端接收时进行解压。网络层面可以对传输报文进行压缩,但不丢失信息。</p> +<p>比如大家都很熟悉的gzip压缩算法,不过,压缩算法有好几种:</p> +<ul> +<li>gzip</li> +<li>compress</li> +<li>deflate</li> +<li>br</li> +</ul> +<p>其中gzip也是压缩率最高的了,此处就以gzip为例。</p> +<p>在网络层面,将wasm文件以gzip压缩,减少其在传输时的体积。虽然减少了传输时的体积,但是浏览器在拿到压缩后的数据,需要消耗一定性能来解密。</p> +<h3 id="开启gzip">开启GZIP</h3> +<p>开启GZIP其实简单,只要前后端约定好都用gzip就行了。</p> +<p>首先,前端请求wasm文件时,需要在request header中放入浏览器支持的压缩模式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Accept-Encoding: gzip, deflate +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,服务端收到这个请求后就可以给出服务端也支持的压缩模式,并告诉浏览器服务端将会用什么压缩模式。</p> +<p>跟浏览器通信的方式就是将信息塞到respone header里面:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Content-Encoding: gzip +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样就开启GZIP了。</p> +<p>然后,就是浏览器接收到response的body和header,知道后端使用gzip压缩的,那么浏览器就会自动用gzip来解压,拿到完整的数据了。</p> +<h3 id="服务端支持">服务端支持</h3> +<p>或许你会想问,浏览器能自动解密,那服务端怎么自动加密呢?要后端写代码让文件加密吗?</p> +<p>那当然不是了,直接让http server来完成这个操作。此处以耳熟能详的Nginx为例。</p> +<p>最简单的就是一行配置开启gzip了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span></code></pre></td></tr></table> +</div> +</div><p>也可以指定gzip的一些参数,比如可以加密的类型、最小加密长度等等:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span><span class="line"><span class="cl">gzip_types text/plain application/xml; +</span></span><span class="line"><span class="cl">gzip_proxied no-cache no-store private expired auth; +</span></span><span class="line"><span class="cl">gzip_min_length 1000; +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>更多的http server配置,可以去各自官方文档查阅。</p> +<h2 id="物理层面">物理层面</h2> +<p>你可能会惊奇,什么物理层面?!</p> +<p>没错,真就是物理层面——直接对wasm文件进行gzip物理压缩!哈哈,这个方法也真是绝了,我之前在Go开发wasm时,寻找减少体积的时候发现的,如果你的wasm已经优化得穷途末路了,不妨大胆试试这个方案。😆</p> +<p>还记得上面章节“网络层面“中,有个问题就是是否需要手动压缩,那么这里就是全程手动压缩和解压缩了,哈哈。</p> +<h3 id="物理压缩">物理压缩</h3> +<p>首先,是对wasm文件进行物理层面的gzip压缩,此处先使用原始的wasm(23347b):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gzip -c pkg/hello_wasm_bg.wasm &gt; pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,看一下其体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">10403</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果卓群,从23347b减少到了10403b!</p> +<p>然后来把上述“代码层面”的优化来一遍,看一下最后的体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">9237</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果更加卓群了,从19871b减少到了9237b了!</p> +<p>所以,此处就是对wasm文件进行物理压缩并存储,然后浏览器请求时,直接请求到<code>.gz</code>文件。</p> +<h3 id="物理解压">物理解压</h3> +<p>浏览器拿到<code>.gz</code>文件后,需要物理解压。</p> +<p>这里推荐使用<code>pako</code>这个前端库,对<code>.gz</code>文件进行解压:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">gunzipWasm</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;target.gz&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">arrayBuffer</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// A fetched response might be decompressed twice on Firefox. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// See https://bugzilla.mozilla.org/show_bug.cgi?id=610679 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">buffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x1F</span> <span class="o">&amp;&amp;</span> <span class="nx">buffer</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x8B</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">buffer</span> <span class="o">=</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">buffer</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>之后就可以直接使用了。</p> +<h2 id="buff叠加">BUFF叠加</h2> +<p>此处直接将上述所有方法都用起来,直接叠加buff,来看看当前(本系列文章积累的)这个案例能减少多少体积。在“物理层面”章节中,已经累加除了“网络层面”的buff了,所以可以直接使用其结果。而“网络层面”章节中,以gzip来压缩,将gzip的压缩率以40%来估算。</p> +<p>那么最终该案例的wasm体积将在5542b,压缩率大约在77%!</p> +<p>当然,还要算上一个初始的语言buff——Rust,使用Rust本身就已经导致wasm文件体积很小了。</p> +<h2 id="总结">总结</h2> +<p>本片文章中,从代码层面、网络层面、物理层面共三个层面介绍了对wasm文件的体积优化方案,其中共有四个方案。</p> +<p>最后,当前(本系列文章积累的)该案例叠加了所有buff之后,能够减少77%的体积,真的感觉挺棒的了,哈哈。</p> +<p>希望能够对各位有所帮助。</p> + + + + 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + Tue, 27 Jun 2023 00:11:12 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(二)——DOM和类型转换》</a>中,描述了使用Rust操作DOM,并实现Rust与JS类型转换的多种方法。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>基于上一篇文章中,Rust与JS的类型转换的多种方法,本篇文章继续深入Rust与JS的交互。</p> +<p>首先,Rust与JS的类型转换,可以实现变量的传递,那么变量是要用在哪里呢?那必然是函数了!</p> +<p>所以,本篇文章来讲述一下Rust与JS的函数相互调用,基于此,可以实现大量日常功能开发。</p> +<p>并且,还将会讲述一下,如何导出Rust的struct给JS调用。</p> +<p>是的,没错,在JS调用Rust的struct!一开始看到这个功能的时候,我的脑子是有点炸裂的&hellip;&hellip;😳</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="函数的相互调用">函数的相互调用</h2> +<h3 id="js调用rust函数">JS调用Rust函数</h3> +<p>其实,在本系列文章的第一篇中,就是使用的JS调用Rust函数作为案例来演示的,这里依然以此为例,主要讲一下要点。</p> +<p>首先,声明一个Rust函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处需要注意的要点如下:</p> +<ul> +<li>引入<code>wasm_bindgen</code></li> +<li>声明一个函数,使用<code>pub</code>声明</li> +<li>在函数上使用<code>#[wasm_bindgen]</code>宏来将Rust函数导出为WebAssembly模块的函数</li> +</ul> +<p>接着,编译成wasm文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,在JS中调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server,在浏览器的控制台中可以看到<code>the result from rust is: 3</code>,表明调用成功!</p> +<h3 id="rust调用js函数">Rust调用JS函数</h3> +<p>###1 指定JS对象</p> +<p>在Rust中调用JS函数,需要进行指定JS对象,也就是说,得明确告诉Rust,这个JS函数是从JS哪儿拿来的用的。</p> +<p>主要在于下面两个方式:</p> +<ul> +<li><em><strong>js_namespace</strong></em>: 是一个可选的属性,用于指定一个JavaScript命名空间,其中包含将要在wasm模块中导出的函数。如果没有指定<code>js_namespace</code>,则所有的导出函数将被放置在全局命名空间下。</li> +<li><em><strong>js_name</strong></em>: 是另一个可选属性,它用于指定JavaScript中的函数名称。如果没有指定<code>js_name</code>,则导出函数的名称将与Rust中的函数名称相同。</li> +</ul> +<p>###2 JS原生函数</p> +<p>对于一些JS原生函数,在Rust中,需要去寻找替代方案,比如我们上一篇文章中讲的<code>console.log()</code>函数,是不是觉得好麻烦啊!</p> +<p>那么,你想直接在Rust中调用JS原生函数吗?!</p> +<p>此处,就以<code>console.log()</code>函数为例,直接在Rust中引入并调用,免去替代方案的烦恼。</p> +<p>首先,给出Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如上代码中,<code>call_js_func</code>函数,顾名思义,此处是调用了js函数,并传入参数<code>hello, javascript!</code>。</p> +<p>那么,<code>call_js_func</code>函数上方的代码,我们来一步步解析一下:</p> +<ol> +<li>第一行代码<code>#[wasm_bindgen]</code>是Rust的属性,它告诉编译器将函数导出为WebAssembly模块</li> +<li><code>extern &quot;C&quot;</code>是C语言调用约定,它告诉Rust编译器将函数导出为C语言函数</li> +<li><code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的console对象</li> +<li><code>fn log(message: &amp;str)</code>是一个Rust函数,它接受一个字符串参数,并将其打印到JavaScript中的console对象中</li> +</ol> +<p>此处与JS交互的关键是<code>js_namespace</code>。在Rust中,<code>js_namespace</code>是用于指定JavaScript命名空间的属性。在WebAssembly中,我们可以通过它将函数绑定到JavaScript中的对象上。</p> +<p>在上述代码中,<code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的<code>console</code>对象。这意味着在JS中使用<code>console.log()</code>函数来调用Rust中的<code>log()</code>函数。</p> +<p>因此,类似的原生函数,都可以使用该方法来实现调用。</p> +<p>最后,我们在JS中调用下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">call_js_func</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">call_js_func</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以在浏览器的控制台中看到<code>hello, javascript!</code>。妙啊!</p> +<p>其实对于<code>console.log()</code>而言,还有另一种调用方式,那就是使用<code>js_namespace</code>和<code>js_name</code>同时指定。</p> +<p>或许,你会问,这有什么不同吗?是的,这有些不同。</p> +<p>不知道你是否发现,当前这个案例中,指定了<code>js_namespace</code>为<code>console</code>,但是真实执行的函数是<code>log()</code>,那么这个<code>log</code>函数的指定,其实是体现在Rust中同样的函数名<code>log</code>。也就是说,该案例的<code>log()</code>就是<code>console.log()</code>中的<code>log()</code>。</p> +<p>我们来换个名字看看,将原来的<code>log()</code>换成<code>log2()</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log2</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log2</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译后去控制台看看,就会看到报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">TypeError: console.log2 is not a function +</span></span></code></pre></td></tr></table> +</div> +</div><p>因此,当我们使用<code>js_namespace</code>和<code>js_name</code>结合的方式,在此处是可以进行自定义函数名的。</p> +<p>看一下Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console, js_name = log)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log_str</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log_str</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,重新定义了一个函数<code>log_str</code>,但是其指定了<code>js_namespace = console</code>和<code>js_name = log</code>,那么此处,就可以使用自定义的函数名。</p> +<p>直接编译后,在控制台看一下,可以直接看到正常输出:<code>hello, javascript!</code>。</p> +<p>总结一下,如果没有指定<code>js_name</code>,则 Rust 函数名称将用作 JS 函数名称。</p> +<p>###3 自定义JS函数</p> +<p>在一定场景下,需要使用Rust调用JS函数,比如对于一些对于JS而言更有优势的场景——用JS操作DOM,用Rust计算。</p> +<p>首先,创建一个文件<code>index.js</code>,写入一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">addIt</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当前的文件结构关系如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── Cargo.lock +</span></span><span class="line"><span class="cl">├── Cargo.toml +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── index.html +</span></span><span class="line"><span class="cl">├── index.js +</span></span><span class="line"><span class="cl">├── pkg +</span></span><span class="line"><span class="cl">│   ├── README.md +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.js +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">│   └── package.json +</span></span><span class="line"><span class="cl">├── src +</span></span><span class="line"><span class="cl">│   └── lib.rs +</span></span><span class="line"><span class="cl">└── target +</span></span><span class="line"><span class="cl"> ├── CACHEDIR.TAG +</span></span><span class="line"><span class="cl"> ├── debug +</span></span><span class="line"><span class="cl"> ├── release +</span></span><span class="line"><span class="cl"> └── wasm32-unknown-unknown +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>index.js</code>和<code>lib.rs</code>,以及<code>hello_wasm_bg.wasm</code>都是不在同一级别的,<code>index.js</code>都在其它两个文件的上一级。记住这个机构关系!</p> +<p>然后,在<code>lib.rs</code>中,指定函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl">##<span class="p">.</span><span class="o">/</span><span class="n">index</span><span class="p">.</span><span class="n">js</span><span class="s">&#34;)] +</span></span></span><span class="line"><span class="cl"><span class="s">extern &#34;</span><span class="n">C</span><span class="s">&#34; { +</span></span></span><span class="line"><span class="cl"><span class="s"> fn addIt(m: i32, n: i32) -&gt; i32; +</span></span></span><span class="line"><span class="cl"><span class="s">} +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>raw_module = &quot;../index.js&quot;</code>的意思是,指定对应的<code>index.js</code>文件,大家应该清楚,此处指定的是刚刚创建的<code>index.js</code>。<code>raw_module</code>的作用就是用来指定js文件的。</p> +<p>这段代码在前端,可以等同于:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addIt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;../index.js&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样在前端都不用引入了,直接在Rust中引入了,感觉还有点奇妙的。</p> +<p>接着,在Rust调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addIt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在前端调用,编译后,在浏览器的控制台中可以看到输出结果了!</p> +<p>总结一下,这里有几个注意点:</p> +<ol> +<li>JS的函数必须要export,否则将无法调用;</li> +<li><code>raw_module</code>只能用来指定相对路径,并且,大家可以在浏览器的控制台中注意到,此处的<code>../</code>的相对路径,其实是以wasm文件而言的相对路径,这里一定要注意呀!</li> +</ol> +<h2 id="js调用rust的struct">JS调用Rust的struct</h2> +<p>现在,来点炸裂的,JS调用Rust的struct?!</p> +<p>JS中连struct都没有,这玩意儿导出来会是什么样,得怎么在JS中调用呢?!</p> +<p>首先,定义一个struct,并且声明几个方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(constructor)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_user</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;name is : </span><span class="si">{}</span><span class="s">, age is : </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="p">).</span><span class="n">as_str</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">set_age</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">age</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,声明了一个struct名为<code>User</code>,包含<code>name</code>和<code>age</code>两个字段,并声明了<code>new</code>、<code>print_user</code>和<code>set_age</code>方法。</p> +<p>其中还有一个未见过的<code>#[wasm_bindgen(constructor)]</code>,<code>constructor</code>用于指示被绑定的函数实际上应该转换为调用 JavaScript 中的 new 运算符。或许你还不太清晰,继续看下去,你就会明白了。</p> +<p>接着,在JS中调用这个struct,和其方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">addIt2</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">User</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;kuari&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">set_age</span><span class="p">(</span><span class="mi">21</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">print_user</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到,这里的用法就很熟悉了!</p> +<p>大概想一下,在Rust中要如何调用?也就是直接new一个——<code>User::new('kuari', 20)</code>。</p> +<p>此处在JS中,也是如此,先new一个!</p> +<p>然后很自然地调用struct的方法。</p> +<p>编译后,打开浏览器,可以在控制台看到输出:<code>name is : kuari, age is : 21</code>。</p> +<p>其实,或许大家会很好奇,起码我是非常好奇的,Rust的struct在JS中到底是一个怎样的存在呢?</p> +<p>这里直接添加一个<code>console.log(user)</code>,就可以在输出看到。那么到底在JS中是一个怎样的存在呢?请各位动手打印一下看看吧!:P</p> +<h2 id="总结">总结</h2> +<p>本篇文章中,主要讲述了Rust与JS的交互,体现在Rust与JS的相互调用,这是建立在<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >上一篇文章</a>中类型转换的基础上的。</p> +<p>Rust与JS的函数相互调用的学习成本还是较大的,而且对比Go写wasm,Rust的颗粒度是非常细的,几乎可以说是随心所欲了。</p> +<p>比较炸裂的就是Rust的struct导出给JS用,这对于Rust与JS的交互而言,还是非常棒的体验。</p> + + + + 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + Sun, 18 Jun 2023 18:18:31 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/72" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(一)——快速开始》</a>中,描述了如何创建项目和快速生成wasm并在前端中使用,迈出了整花活儿的第一步。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +<li>web-sys 0.3.64</li> +</ul> +<h2 id="dom">DOM</h2> +<h3 id="配置依赖">配置依赖</h3> +<p>要操作DOM,需要引入新的依赖<code>web-sys</code>,因此,可以配置<code>Cargo.toml</code>中依赖如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>你或许会好奇,这个<code>features</code>是什么,讲真,我一开始很好奇,又没看到什么特别的说明,试错才发现,原来是要手动引入功能依赖&hellip;比如说,当你需要在Rust中使用JS的<code>console</code>,那么你需要在<code>features</code>中加入<code>console</code>。</p> +<h3 id="获取document">获取Document</h3> +<p>在Rust中使用<code>Document</code>,我们需要按照上一步的说明,添加<code>features</code>。那么这里有一个依赖关系,首先在Rust中获取<code>window</code>,然后再获取<code>document</code>。</p> +<p>因此,添加<code>features</code>后如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后在<code>lib.rs</code>中创建一个函数,用来调用<code>document</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>那么,现在就是在Rust中解锁了<code>document</code>,就可以在前端为所欲为了!</p> +<h3 id="操作element">操作Element</h3> +<p>那么开始操作一波,首先得获取到<code>Element</code>&hellip;&hellip;</p> +<p>是的,你没有想错,继续来添加<code>features</code>吧,此处要添加一个<code>Element</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>ok,那么继续。此处设定函数传入两个参数<code>selector</code>和<code>message</code>,然后通过<code>selector</code>获取element,更新值为<code>message</code>参数的值。完整函数如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">(</span><span class="n">selector</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">document</span><span class="p">.</span><span class="n">query_selector</span><span class="p">(</span><span class="n">selector</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load element&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">element</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">element</span><span class="p">.</span><span class="n">set_inner_html</span><span class="p">(</span><span class="n">message</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">panic!</span><span class="p">(</span><span class="s">&#34;Failed to set inner html&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编译">编译</h3> +<p>将写完的Rust项目编译成wasm:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在html中调用">在html中调用</h3> +<p>基于上一篇文章的项目中的html,此处添加一个<code>div</code>,id为<code>message</code>,添加调用wasm的<code>update_message</code>函数,代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;message&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">update_message</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> <span class="c1">// 引入update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">update_message</span><span class="p">(</span><span class="s1">&#39;#message&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;h1&gt;Hello, Rust!&lt;/h1&gt;&#39;</span><span class="p">);</span> <span class="c1">// 调用update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在浏览器验证">在浏览器验证</h3> +<p>启动一个http server,然后在浏览器查看,可以看到在页面上出现一个<code>h1</code>标签的<code>Hello, Rust!</code>。</p> +<h3 id="发现更多方法">发现更多方法</h3> +<p>按照文章来写的过程中,你应该会发现一个问题——怎么这些方法没有补全?!</p> +<p>是的,没错的,(至少我发现)当前<code>web-sys</code>并没有补全,所以只能结合开发者优秀的前端技能和丰富的<a class="link" href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/" target="_blank" rel="noopener" + >官方文档</a>来开发了。</p> +<h2 id="rust与js的类型相互转换">Rust与JS的类型相互转换</h2> +<p>对于wasm而言,性能固然是提升的,但是类型转换一直是个问题。当大量数据需要在wasm/js中进行类型转换时,这对性能来说,真的是个灾难。之前在使用go开发wasm时,就遇到过这样的问题,需要用官方的方法来进行手动类型转换,然而wasm处理的是一个很大的数据量&hellip;&hellip;</p> +<p>不过好在Rust的类型支持真的挺丰富的!</p> +<h3 id="基础类型">基础类型</h3> +<p>基础类型挺简单的,而且Rust的范性也很好地支持了很多类型。如下是基础类型映射表:</p> +<table> +<thead> +<tr> +<th>Rust类型</th> +<th>JavaScript类型</th> +</tr> +</thead> +<tbody> +<tr> +<td>i8</td> +<td>number</td> +</tr> +<tr> +<td>i16</td> +<td>number</td> +</tr> +<tr> +<td>i32</td> +<td>number</td> +</tr> +<tr> +<td>i64</td> +<td>BigInt</td> +</tr> +<tr> +<td>u8</td> +<td>number</td> +</tr> +<tr> +<td>u16</td> +<td>number</td> +</tr> +<tr> +<td>u32</td> +<td>number</td> +</tr> +<tr> +<td>u64</td> +<td>BigInt</td> +</tr> +<tr> +<td>f32</td> +<td>number</td> +</tr> +<tr> +<td>f64</td> +<td>number</td> +</tr> +<tr> +<td>bool</td> +<td>boolean</td> +</tr> +<tr> +<td>char</td> +<td>string</td> +</tr> +<tr> +<td>&amp;str</td> +<td>string</td> +</tr> +<tr> +<td>String</td> +<td>string</td> +</tr> +<tr> +<td>&amp;[T] 例如:&amp;[u8]</td> +<td>[T] 例如:Uint8Array</td> +</tr> +<tr> +<td>Vec<T></td> +<td>Array</td> +</tr> +</tbody> +</table> +<h3 id="基础类型转换示例">基础类型转换示例</h3> +<p>在<code>lib.rs</code>文件中,创建一个函数,挑选几个类型作为参数传入,然后将其读取并打印:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到该函数传入了JS的<code>number</code>、<code>boolean</code>、<code>Uint8Array</code>和<code>Array</code>四个类型的参数。</p> +<p>然后编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在前端中引入函数并调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_values</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_values</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">,</span> <span class="nx">jsBoolean</span><span class="p">,</span> <span class="nx">jsUint8Array</span><span class="p">,</span> <span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server并打开浏览器,在控制台可以看到&hellip;看不到?!</p> +<p>是的,没错,Rust的<code>println!</code>只会将打印的内容发送到Rust的标准输出流,而不是前端的控制台。如果想在控制台中打印,那么需要调用JS的<code>console</code>了。</p> +<p>使用新的功能,第一步就是添加<code>features</code>,<code>Cargo.toml</code>中添加<code>console</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在Rust中调用<code>console.log()</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="s">&#34;Hello, Rust!&#34;</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处将其封装成一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">console_log</span><span class="p">(</span><span class="n">message</span>: <span class="nb">String</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,将示例函数的<code>println</code>改成<code>console_log()</code>和<code>format!</code>,函数代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,编译之后,打开浏览器,就可以在控制台看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">js number: 10 +</span></span><span class="line"><span class="cl">js boolean: true +</span></span><span class="line"><span class="cl">js Uint8Array item: 1 +</span></span><span class="line"><span class="cl">js Uint8Array item: 2 +</span></span><span class="line"><span class="cl">js Uint8Array item: 3 +</span></span><span class="line"><span class="cl">js number array item: 30 +</span></span><span class="line"><span class="cl">js number array item: 40 +</span></span><span class="line"><span class="cl">js number array item: 50 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="通用类型">通用类型</h3> +<p>Rust中提供了一个通用的类型——JsValue,可以作为任何JS类型。</p> +<p>这里给一个简单的案例,设置一个函数,使用<code>JsValue</code>作为参数传入,并打印。</p> +<p>创建函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_js_value</span><span class="p">(</span><span class="n">val</span>: <span class="nc">JsValue</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">val</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译成wasm文件。</p> +<p>在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_js_value</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsBoolean</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsUint8Array</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在html中,传入了不同类型的参数,但是在浏览器的控制台中可以看到,将所有不同类型的参数都打印出来了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">JsValue(10) +</span></span><span class="line"><span class="cl">JsValue(true) +</span></span><span class="line"><span class="cl">JsValue(Uint8Array) +</span></span><span class="line"><span class="cl">JsValue([30, 40, 50]) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="result">Result</h3> +<p><code>Result</code>在Rust中是一个很重要的存在,经常写Rust的话,也不想在写WebAssembly时改变开发习惯。</p> +<p>其实对于JS而言,<code>Result</code>可以直接在<code>catch</code>中捕获到,只是说,这里我们需要定义好参数类型。</p> +<p>####1 使用Result返回报错</p> +<p>首先来一个只返回报错的场景:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">only_return_error_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>这里返回类型是<code>Result</code>,但是仅仅返回了一个错误。值得注意的是,这里的报错使用的类型是<code>JsError</code>,当然,这里也可以使用<code>JsValue</code>。</p> +<p>然后在html调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">only_return_error_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;1 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;100 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>那么,在浏览器的控制台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">100 is ok +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 使用Result返回正常值和错误</p> +<p>那么,如果想既返回正常值,也想返回错误呢?Rust返回一个<code>Result</code>是没有问题,那么JS怎么解析呢?</p> +<p>直接上Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_all_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>该函数,获取到参数后,如果满足条件,加10后返回,否则报错。</p> +<p>那么看看html中如何调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_all_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,正常获取就行了&hellip;&hellip;/捂脸哭</p> +<p>这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>最后,就是在浏览器的控制台中看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">get 110 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="直接引入js类型">直接引入JS类型</h3> +<p>如果你想更直接一点,那么可以直接引入JS类型!这里主要是利用<code>js-sys</code>这个依赖,可以在<a class="link" href="https://docs.rs/js-sys/latest/js_sys/" target="_blank" rel="noopener" + >官方文档</a>上看到很多JS的类型和函数,直接引入即可使用。当然,一定场景下,直接引入的类型,是需要手动转换类型的。</p> +<p>####1 配置依赖</p> +<p>在<code>Cargo.toml</code>中添加<code>js-sys</code>依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="nx">js-sys</span> <span class="p">=</span> <span class="s2">&#34;0.3.61&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 Uint8Array</p> +<p>首先以<code>Uint8Array</code>举例,在<code>lib.rs</code>头部引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Uint8Array</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后创建一个函数,参数和返回都是<code>Uint8Array</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_uint8_array</span><span class="p">(</span><span class="n">js_arr</span>: <span class="nc">Uint8Array</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Uint8Array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// new Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">new_with_length</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Uint8Array -&gt; vec +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">)</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">to_vec</span><span class="p">().</span><span class="n">iter</span><span class="p">().</span><span class="n">enumerate</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Avoid type conversion +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">js_arr</span><span class="p">.</span><span class="n">length</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">get_index</span><span class="p">(</span><span class="n">index</span><span class="p">)));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// vec -&gt; Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">arr2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">from</span><span class="p">(</span><span class="n">vec</span><span class="p">.</span><span class="n">as_slice</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">arr2</span><span class="p">.</span><span class="n">clone</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">arr</span><span class="p">.</span><span class="n">set_index</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>忽略该函数中无意义的逻辑和<code>arr</code>变量的警告,只是为了演示用法。</p> +</blockquote> +<p>可以在代码中看到,直接引入的<code>Uint8Array</code>有自己的方法,一定场景下,需要转换类型,但是最好避免进行类型转换,而直接使用其自带的方法。</p> +<p>这里可以简要总结下,就是最好一定场景内全部使用直接引入的JS类型,或者直接全部使用Rust类型来代替JS类型,两者都存在场景下,手动转换类型是件很糟糕的事。</p> +<p>####3 Date</p> +<p><code>Date</code>类型,在上面的篇章中都没有提及,这里可以直接引入JS的<code>Date</code>类型来使用。</p> +<p>首先是引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Date</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,创建一个函数,返回时间戳:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_time</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Date</span>::<span class="n">new_0</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">date</span><span class="p">.</span><span class="n">get_time</span><span class="p">()</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_time</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;current time: &#39;</span><span class="p">,</span> <span class="nx">return_time</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在浏览器的控制台中,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">current time: 1686979932833 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="总结">总结</h2> +<p>本文中,主要讲述了如何使用Rust来实现DOM操作,读者可以根据方法自己去找到合适的方法,来实现自己的场景。其次,还讲述了Rust与JS的类型转换,从基础的各自类型的映射,到Rust独有的<code>Result</code>,到直接引入JS类型。当然这里需要注意的是,直接引入JS类型和Rust的基础类型映射JS类型这两种方法尽量不要混用,混用会导致需要手动类型转换,造成性能损耗。</p> +<p>至此,又向Rust和WebAssembly整花活儿迈进了一步~😼</p> + + + + 使用Rust和WebAssembly整花活儿(一)——快速开始 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + Wed, 14 Jun 2023 17:32:28 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(一)——快速开始" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<p>之前写过一篇文章,是关于如何使用golang来开发WebAssembly的——<a class="link" href="https://blog.hunterji.com/p/webassembly--%e6%9c%aa%e6%9d%a5%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e7%9a%84%e5%bf%85%e5%a4%87%e6%8a%80%e8%83%bd" target="_blank" rel="noopener" + >WebAssembly:未来前端开发的必备技能</a>。</p> +<p>Rust和Go都可以用来开发WebAssembly,但它们有各自的优势和劣势。</p> +<p>Rust的优点:</p> +<ul> +<li>更快的性能和更小的二进制文件</li> +<li>更好的内存安全性</li> +</ul> +<p>Go的优点:</p> +<ul> +<li>更容易上手和学习</li> +<li>更好的生态系统和社区支持</li> +</ul> +<p>综合来说,如果你更注重性能和内存安全性,那么Rust可能是更好的选择。而如果你更注重开发效率和易用性,那么Go可能更适合你。当然,实际情况还需要根据具体的项目需求和团队情况来选择。</p> +<p>因为一些工作需求,最近整了些rust的花活儿,这里系统地记录一下。当你遇到更注重性能和内存安全性的场景,希望这能有帮助。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="创建项目并添加依赖">创建项目并添加依赖</h2> +<p>此处默认已经安装Rust,需要安装的小伙伴儿可以参考<a class="link" href="https://www.rust-lang.org/tools/install" target="_blank" rel="noopener" + >官网</a>。</p> +<p>使用Cargo创建一个名为<code>hello-wasm</code>的项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo new --lib hello-wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>进入项目,打开文件<code>Cargo.toml</code>,添加依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="更新librs">更新lib.rs</h2> +<p>默认创建项目中,存在一个名为<code>lib.rs</code>的文件,将内容全部替换成:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="编译">编译</h2> +<p>至此,我们创建了一个最简单的功能——一个返回两个整数相加结果的函数。</p> +<p>然后我们可以进行编译了,编译之前需要安装一个工具<code>wasm-pack</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo install wasm-pack +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>编译完成之后,将会多出来一个<code>pkg</code>文件夹,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pkg +</span></span><span class="line"><span class="cl">├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">├── hello_wasm.js +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">└── package.json +</span></span></code></pre></td></tr></table> +</div> +</div><p>虽然文件很多,但是首先我们看到了我们所需要的wasm文件,并且,根据go的wasm引入方式,这里我们或许会需要用到js文件。</p> +<h2 id="前端引入">前端引入</h2> +<p>为了方便最快校验,直接在<code>hello-wasm</code>项目中创建<code>index.html</code>文件,来进行前端引入。</p> +<h3 id="创建indexhtml">创建index.html</h3> +<p>那么,首先,创建<code>index.html</code>文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错!这是一场标准的开局!😼</p> +<h3 id="引入wasm">引入WASM</h3> +<p>其实不同于go语言的wasm引入方式,Rust更希望直接引入js文件,而不是让开发者手动引入wasm文件。</p> +<p>这里使用js引入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="完整代码">完整代码</h3> +<p>完整的html代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="验证">验证</h2> +<p>这里可以快速起一个http server,这里我选择使用<code>http-server</code>,也可以使用<code>python3 -m http.server</code>这样的方式,看怎么各自的使用习惯。</p> +<p>那么,启动http server:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">http-server +</span></span></code></pre></td></tr></table> +</div> +</div><p>打开浏览器,访问<code>http://localhost:8080</code>,打开调试器,即可看到输出<code>the result from rust is: 3</code>,这就意味着迈出了整花活儿的第一步!</p> +<p><img src="https://user-images.githubusercontent.com/25321169/245747861-4fb71bd2-9f90-41b3-ada8-6e1d930044d4.png" + + + + loading="lazy" + + alt="rust_wasm" + + +></p> +<h2 id="常见问题">常见问题</h2> +<h3 id="前端报响应类型错误">前端报响应类型错误</h3> +<p>详细报错如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Failed to load module script: The server responded with a non-JavaScript MIME type of &#34;application/wasm&#34;. Strict MIME type checking is enforced for module scripts per HTML spec. +</span></span></code></pre></td></tr></table> +</div> +</div><p>当引入WebAssembly生成的js文件时,可能会遇到这个报错。报错乍一看是http server的响应问题,或者搜索时候,也会有帖子说这是一个response问题。</p> +<p>实际上,当按照这个文章一步步操作时是不会有这个问题的,是因为本文的编译参数是直接解决了这个问题的。当我自己摸索的时候,解决这个问题真的是看到人都麻了……</p> +<p>关键在于编译命令的参数:<code>--target</code>。</p> +<p>当没有设置这个参数时,默认的参数其实是<code>--target bundler</code>,其是编译成给webpack之类的脚手架使用的。因此这里使用<code>—target web</code>,则是使其编译成可直接在web中使用。</p> +<p>相关参数如下:</p> +<ul> +<li><strong>bundler</strong>:编译成给webpack之类的脚手架使用</li> +<li><strong>web</strong>:编译成web可直接使用</li> +<li><strong>nodejs</strong>:编译成可通过require来加载的node模块</li> +<li><strong>deno</strong>:编译成可通过import加载的deno模块</li> +<li><strong>no-modules</strong>:跟web类似,但是更旧,且不能使用es模块</li> +</ul> +<h3 id="直接引入wasm文件">直接引入wasm文件</h3> +<p>若此时尝试直接引入wasm文件,而不是使用本文所述的方式,那么你会发现,也是可行的!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;./pkg/hello_wasm_bg.wasm&#34;</span><span class="p">),</span> <span class="p">{}).</span><span class="nx">then</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;the result from rust is: &#39;</span><span class="p">,</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,当前是可行的,但是当引入了一些别的比如dom之类的,就坏起来了……</p> +<h3 id="更新的wasm引入方式">更新的wasm引入方式</h3> +<p>上一问题中,且不说是否可以直接引入wasm文件,这里仅说一下,<code>instantiateStreaming</code>这个方法。这是一个更新的方法,无需转成<code>arrayBuffer</code>,这也是摸索Rust整活儿时候发现的。如果在别的语言引入wasm,请使用这个<a class="link" href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming" target="_blank" rel="noopener" + >更新的方法</a>吧。</p> + + + + + diff --git a/tags/rust/page/1/index.html b/tags/rust/page/1/index.html new file mode 100644 index 0000000..99b1000 --- /dev/null +++ b/tags/rust/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/rust/ + + + + + + diff --git a/tags/swift/index.html b/tags/swift/index.html new file mode 100644 index 0000000..7d07dba --- /dev/null +++ b/tags/swift/index.html @@ -0,0 +1,559 @@ + + + + +Tag: Swift - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

3 pages

+

Swift

+ +
+
+
+ +
+ + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/swift/index.xml b/tags/swift/index.xml new file mode 100644 index 0000000..e9bd594 --- /dev/null +++ b/tags/swift/index.xml @@ -0,0 +1,558 @@ + + + + Swift on 开发者小橙 + https://blog.hunterji.com/tags/swift/ + Recent content in Swift on 开发者小橙 + Hugo -- gohugo.io + en-us + Mon, 30 Aug 2021 14:29:02 +0000 + IOS监听上下左右滑动手势 + https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/ + Mon, 30 Aug 2021 14:29:02 +0000 + + https://blog.hunterji.com/p/ios%E7%9B%91%E5%90%AC%E4%B8%8A%E4%B8%8B%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8%E6%89%8B%E5%8A%BF/ + <h2 id="前言">前言</h2> +<p>IOS监听手势使用的方法为<code>UISwipeGestureRecognizer</code>。</p> +<h2 id="添加手势监听">添加手势监听</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">gesture</span> <span class="p">=</span> <span class="n">UISwipeGestureRecognizer</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">addTarget</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="k">#selector</span><span class="p">(</span><span class="n">yourSelector</span><span class="p">(</span><span class="n">gesture</span><span class="p">:)))</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">direction</span> <span class="p">=</span> <span class="p">.</span><span class="kr">left</span> <span class="c1">// .left左滑 .right右滑 .up上滑 .down下滑</span> +</span></span><span class="line"><span class="cl"><span class="kc">self</span><span class="p">.</span><span class="n">addGestureRecognizer</span><span class="p">(</span><span class="n">gesture</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="添加响应事件">添加响应事件</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kr">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">leftPushEvent</span><span class="p">(){</span> +</span></span><span class="line"><span class="cl"> <span class="bp">print</span><span class="p">(</span><span class="s">&#34;响应...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="模板">模板</h2> +<p>把上面的整合起来,基本可以按照这个模板来写。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kr">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">leftPushEvent</span><span class="p">(){</span> +</span></span><span class="line"><span class="cl"> <span class="bp">print</span><span class="p">(</span><span class="s">&#34;响应...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">gesture</span> <span class="p">=</span> <span class="n">UISwipeGestureRecognizer</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">addTarget</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="k">#selector</span><span class="p">(</span><span class="n">leftPushEvent</span><span class="p">(</span><span class="n">gesture</span><span class="p">:)))</span> +</span></span><span class="line"><span class="cl"><span class="n">gesture</span><span class="p">.</span><span class="n">direction</span> <span class="p">=</span> <span class="p">.</span><span class="kr">left</span> +</span></span><span class="line"><span class="cl"><span class="kc">self</span><span class="p">.</span><span class="n">addGestureRecognizer</span><span class="p">(</span><span class="n">gesture</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://www.jianshu.com/p/30104b1c872d" target="_blank" rel="noopener" + >iOS手势识别&ndash;上下左右滑动</a></li> +</ul> + + + + Swift计算两个日期的天数差 + https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/ + Mon, 07 Jun 2021 15:12:29 +0000 + + https://blog.hunterji.com/p/swift%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E6%97%A5%E6%9C%9F%E7%9A%84%E5%A4%A9%E6%95%B0%E5%B7%AE/ + <h2 id="官方方法">官方方法</h2> +<h3 id="datecomponents"><strong>DateComponents</strong></h3> +<p>A date or time specified in terms of units (such as year, month, day, hour, and minute) to be evaluated in a calendar system and time zone.</p> +<p>以要在日历系统和时区中计算的单位(例如年、月、日、小时和分钟)指定的日期或时间。</p> +<h2 id="实现">实现</h2> +<h3 id="计算两个字符串形式的日期的天数差">计算两个字符串形式的日期的天数差</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">dateDiff</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 计算两个日期差,返回相差天数</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">calendar</span> <span class="p">=</span> <span class="n">Calendar</span><span class="p">.</span><span class="n">current</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateFormat</span> <span class="p">=</span> <span class="s">&#34;yyyy-MM-dd&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">today</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 开始日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">startDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-08&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 结束日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">endDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-09&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">diff</span><span class="p">:</span><span class="n">DateComponents</span> <span class="p">=</span> <span class="n">calendar</span><span class="p">.</span><span class="n">dateComponents</span><span class="p">([.</span><span class="n">day</span><span class="p">],</span> <span class="n">from</span><span class="p">:</span> <span class="n">startDate</span><span class="p">!,</span> <span class="n">to</span><span class="p">:</span> <span class="n">endDate</span><span class="p">!)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">diff</span><span class="p">.</span><span class="n">day</span><span class="p">!</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="计算当天跟某一天的天数差">计算当天跟某一天的天数差</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">checkDiff</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 计算两个日期差,返回相差天数</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">calendar</span> <span class="p">=</span> <span class="n">Calendar</span><span class="p">.</span><span class="n">current</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateFormat</span> <span class="p">=</span> <span class="s">&#34;yyyy-MM-dd&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当天</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">today</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">startDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="n">formatter</span><span class="p">.</span><span class="n">string</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="n">today</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 固定日期</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">endDate</span> <span class="p">=</span> <span class="n">formatter</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="s">&#34;2021-06-09&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">diff</span><span class="p">:</span><span class="n">DateComponents</span> <span class="p">=</span> <span class="n">calendar</span><span class="p">.</span><span class="n">dateComponents</span><span class="p">([.</span><span class="n">day</span><span class="p">],</span> <span class="n">from</span><span class="p">:</span> <span class="n">startDate</span><span class="p">!,</span> <span class="n">to</span><span class="p">:</span> <span class="n">endDate</span><span class="p">!)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">diff</span><span class="p">.</span><span class="n">day</span><span class="p">!</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Swift UI项目调用core data + https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/ + Mon, 07 Jun 2021 11:24:22 +0000 + + https://blog.hunterji.com/p/swift-ui%E9%A1%B9%E7%9B%AE%E8%B0%83%E7%94%A8core-data/ + <h2 id="前言">前言</h2> +<p>这篇文章是我写的第一篇Swift UI相关的笔记吧。</p> +<p>这真的难搞哦,毕竟新出来的。国内文档真的是少之又少,国外的文档也真不多,基本搜出来的都是UIKit的。官方文档,也是一言难尽,基本就处于,我要实现一个功能,然后去搜一下各种帖子,筛选掉无用的帖子,找到有用的点,当然也是时常根本找不到有用的帖子,然后就要去油管上看各种教程,然后发现:哦!原来还有这个方法!接着去官方文档搜一下,看一下属性,自己调用调用&hellip;&hellip;</p> +<p>光是这个core data我就折腾了近一周,最后发现,原来还是官方好呀&hellip;..</p> +<p>吐槽一下自学swift ui,现在进入正题了。</p> +<p>core data呢,是苹果官方的本地数据库,但是其存储的文件其实是<code>sqlite</code>文件。其可以通过icloud实现备份和同步,当然icloud我会额外写一篇文档来详细讲述的(又是一把辛酸泪&hellip;)。</p> +<h2 id="环境">环境</h2> +<p>如下是我当前的环境:</p> +<ul> +<li>系统:macOS Big Sur 11.4</li> +<li>Xcode:Version 12.5 (12E262)</li> +<li>Swift:5.4</li> +</ul> +<h2 id="操作步骤">操作步骤</h2> +<h3 id="创建项目">创建项目</h3> +<p>在创建项目的时候,可以直接选择<code>Use Core Data</code>选项,xcode会直接在<code>ContentView.swift</code>中生成一个相关demo。</p> +<p><img src="https://blog.hunterji.com/../assets/create_swiftui_project.png" + + + + loading="lazy" + + alt="创建项目时候截图" + + +></p> +<h3 id="查看相关文件">查看相关文件</h3> +<p>创建之后,查看文件目录,相对于不选择<code>Use Core Data</code>,会多出如下几个文件:</p> +<ul> +<li><Your-Project-Name>.xcdatamodeId</li> +<li>Persistence.swift</li> +</ul> +<h3 id="创建core-data表">创建core data表</h3> +<p>点击<code>&lt;Your-Project-Name&gt;.xcdatamodeId</code>文件,进入页面。</p> +<p>可以看到页面内有默认的<code>CONFIGURATIONS</code>为<code>Default</code>,默认的<code>ENITITIES</code>为<code>Item</code>,可以理解为分别对应sql中的库和表。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_core_data_page.png" + + + + loading="lazy" + + alt="swiftui_core_data_page.png" + + +></p> +<p>然后点击<code>Item</code>,可以看到其字段,有默认的<code>timestamp</code>字段。</p> +<p>若要新增<code>ENTITIES</code>(表),点击底部的<code>Add Entity</code>按钮即可。</p> +<p>若要新增<code>Attributes</code>(字段),点击右侧<code>Attributes</code>中的<code>+</code>即可,注意字段要选择类型。</p> +<p>比如,此处以默认的<code>Item</code>为例,新增<code>username</code>和<code>age</code>两个字段。</p> +<p><img src="https://blog.hunterji.com/../assets/swiftui_core_data_create_attributes.png" + + + + loading="lazy" + + alt="swiftui_core_data_create_attributes.png" + + +></p> +<h3 id="代码层面操作core-data">代码层面操作core data</h3> +<h4 id="1查看">1)查看</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">)</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">viewContext</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="n">FetchRequest</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">sortDescriptors</span><span class="p">:</span> <span class="p">[</span><span class="n">NSSortDescriptor</span><span class="p">(</span><span class="n">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="n">Item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">ascending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)],</span> +</span></span><span class="line"><span class="cl"> <span class="n">animation</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">var</span> <span class="nv">items</span><span class="p">:</span> <span class="n">FetchedResults</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// body中便利items即可</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="2新增">2)新增</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>如上是xcode自动生成的新增函数,若是要自定义添加字段可以这样改动下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// 此处按照如上添加Attributes修改,具体修改按照项目具体情况</span> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="nb">Int16</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">username</span> <span class="p">=</span> <span class="n">username</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="n">age</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3删除">3)删除</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="n">IndexSet</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">offsets</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">items</span><span class="p">[</span><span class="nv">$0</span><span class="p">]</span> <span class="p">}.</span><span class="n">forEach</span><span class="p">(</span><span class="n">viewContext</span><span class="p">.</span><span class="n">delete</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="4汇总">4)汇总</h4> +<p>在默认生成代码中,有一个<code>toolbar</code>,其在<code>macos</code>中可以生效,但是在<code>ios</code>中只有<code>EditionButton()</code>可以使用,为了方便演示,此处新增一个<code>Button</code>来添加数据。</p> +<p>其次有点要声明下,在xcode中写代码时,右侧的<code>canvas</code>会实时渲染,列表中出现的数据并不是<code>core data</code>中的数据,而是默认生成的<code>Persistence.swift</code>中生成的演示数据,只能看看,不能当真。只有在模拟器/实体机编译运行时才能操作<code>core data</code>。</p> +<p>如下为修改过后的<code>ContentView.swift</code>文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span><span class="lnt">71 +</span><span class="lnt">72 +</span><span class="lnt">73 +</span><span class="lnt">74 +</span><span class="lnt">75 +</span><span class="lnt">76 +</span><span class="lnt">77 +</span><span class="lnt">78 +</span><span class="lnt">79 +</span><span class="lnt">80 +</span><span class="lnt">81 +</span><span class="lnt">82 +</span><span class="lnt">83 +</span><span class="lnt">84 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"><span class="c1">// ContentView.swift</span> +</span></span><span class="line"><span class="cl"><span class="c1">// HelloKuari</span> +</span></span><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"><span class="c1">// Created by Kuari on 2021/6/5.</span> +</span></span><span class="line"><span class="cl"><span class="c1">//</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">SwiftUI</span> +</span></span><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">CoreData</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">ContentView</span><span class="p">:</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">Environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">)</span> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">viewContext</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="n">FetchRequest</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="n">sortDescriptors</span><span class="p">:</span> <span class="p">[</span><span class="n">NSSortDescriptor</span><span class="p">(</span><span class="n">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="n">Item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">ascending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)],</span> +</span></span><span class="line"><span class="cl"> <span class="n">animation</span><span class="p">:</span> <span class="p">.</span><span class="k">default</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">var</span> <span class="nv">items</span><span class="p">:</span> <span class="n">FetchedResults</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">VStack</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">List</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Tom: </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">username</span><span class="p">!</span><span class="si">)</span><span class="s"> age: </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">age</span><span class="si">)</span><span class="s"> time : </span><span class="si">\(</span><span class="n">item</span><span class="p">.</span><span class="n">timestamp</span><span class="p">!,</span> <span class="n">formatter</span><span class="p">:</span> <span class="n">itemFormatter</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">onDelete</span><span class="p">(</span><span class="n">perform</span><span class="p">:</span> <span class="n">deleteItems</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 新增一个按钮来添加数据</span> +</span></span><span class="line"><span class="cl"> <span class="n">Button</span><span class="p">(</span><span class="n">action</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="s">&#34;tom&#34;</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="mi">12</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="n">label</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">Text</span><span class="p">(</span><span class="s">&#34;Add Item&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">addItem</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="nb">Int16</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">newItem</span> <span class="p">=</span> <span class="n">Item</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">username</span> <span class="p">=</span> <span class="n">username</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="n">age</span> +</span></span><span class="line"><span class="cl"> <span class="n">newItem</span><span class="p">.</span><span class="n">timestamp</span> <span class="p">=</span> <span class="n">Date</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">deleteItems</span><span class="p">(</span><span class="n">offsets</span><span class="p">:</span> <span class="n">IndexSet</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">withAnimation</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">offsets</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">items</span><span class="p">[</span><span class="nv">$0</span><span class="p">]</span> <span class="p">}.</span><span class="n">forEach</span><span class="p">(</span><span class="n">viewContext</span><span class="p">.</span><span class="n">delete</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="n">viewContext</span><span class="p">.</span><span class="n">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// Replace this implementation with code to handle the error appropriately.</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">nsError</span> <span class="p">=</span> <span class="n">error</span> <span class="k">as</span> <span class="n">NSError</span> +</span></span><span class="line"><span class="cl"> <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;Unresolved error </span><span class="si">\(</span><span class="n">nsError</span><span class="si">)</span><span class="s">, </span><span class="si">\(</span><span class="n">nsError</span><span class="p">.</span><span class="n">userInfo</span><span class="si">)</span><span class="s">&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">let</span> <span class="nv">itemFormatter</span><span class="p">:</span> <span class="n">DateFormatter</span> <span class="p">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nv">formatter</span> <span class="p">=</span> <span class="n">DateFormatter</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">dateStyle</span> <span class="p">=</span> <span class="p">.</span><span class="n">short</span> +</span></span><span class="line"><span class="cl"> <span class="n">formatter</span><span class="p">.</span><span class="n">timeStyle</span> <span class="p">=</span> <span class="p">.</span><span class="n">medium</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">formatter</span> +</span></span><span class="line"><span class="cl"><span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">ContentView_Previews</span><span class="p">:</span> <span class="n">PreviewProvider</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">static</span> <span class="kd">var</span> <span class="nv">previews</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">ContentView</span><span class="p">().</span><span class="n">environment</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">managedObjectContext</span><span class="p">,</span> <span class="n">PersistenceController</span><span class="p">.</span><span class="n">preview</span><span class="p">.</span><span class="n">container</span><span class="p">.</span><span class="n">viewContext</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后点击左侧顶部的运行按钮,编译运行。</p> +<p>一开始是空白一片,点击<code>Add Item</code>按钮之后,便会开始添加数据。</p> +<img src="../assets/swiftui_core_data_add_item_demo.png" alt="swiftui_core_data_add_item_demo.png" style="zoom:50%;" /> +<p>对于记录右滑即可删除,其为<code>List</code>的<code>onDelete</code>方法。</p> +<h2 id="结语">结语</h2> +<p>该文章是面向新手的,也是记录下我踩过的坑,因为目前文档匮乏,身边也没swift的开发小伙伴儿,只能靠自己摸索,若有大佬有更好的方法,真的还请不吝赐教。</p> +<p>后面持续记录踩坑中&hellip;&hellip;</p> + + + + + diff --git a/tags/swift/page/1/index.html b/tags/swift/page/1/index.html new file mode 100644 index 0000000..4bf579e --- /dev/null +++ b/tags/swift/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/swift/ + + + + + + diff --git a/tags/typescript/index.html b/tags/typescript/index.html new file mode 100644 index 0000000..90810bd --- /dev/null +++ b/tags/typescript/index.html @@ -0,0 +1,559 @@ + + + + +Tag: TypeScript - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

3 pages

+

TypeScript

+ +
+
+
+ +
+ + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/typescript/index.xml b/tags/typescript/index.xml new file mode 100644 index 0000000..363ba24 --- /dev/null +++ b/tags/typescript/index.xml @@ -0,0 +1,974 @@ + + + + TypeScript on 开发者小橙 + https://blog.hunterji.com/tags/typescript/ + Recent content in TypeScript on 开发者小橙 + Hugo -- gohugo.io + en-us + Fri, 17 Feb 2023 13:31:56 +0000 + 浏览器上ts实现前端直传minio + https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/ + Fri, 17 Feb 2023 13:31:56 +0000 + + https://blog.hunterji.com/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8Ats%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E7%9B%B4%E4%BC%A0minio/ + <h2 id="前言">前言</h2> +<p>前端从后端获取到sts,然后直接minio,极大减少服务端的压力。</p> +<p>当然,肯定会这种疑问,为什么不在后端生成临时签名url,给前端上传/下载呢?问就是业务需要 /狗头&hellip;&hellip;</p> +<h2 id="环境">环境</h2> +<ul> +<li><em>minio</em>: ^7.0.32</li> +<li><em>typescript</em>: 4.9.5</li> +</ul> +<h2 id="浏览器上的坑">浏览器上的坑</h2> +<p>为什么标题上要强调“浏览器”呢?</p> +<p>这是因为官方的minio.js,感觉当前版本并没有考虑浏览器的使用场景,无法直接在浏览器上直传。</p> +<p>所以这里就是推荐一个折中的方案,可以在前端直传。</p> +<p>当然了,如果业务允许,当前版本请直接在后端生成临时签名url吧!</p> +<h2 id="minio直传代码实现">minio直传代码实现</h2> +<h3 id="安装miniojs">安装minio.js</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pnpm add -D minio +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="生成client">生成client</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">Minio</span> <span class="nx">from</span> <span class="s1">&#39;minio&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">minioClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Minio</span><span class="p">.</span><span class="nx">Client</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">region</span><span class="o">:</span> <span class="s1">&#39;cn-north-1&#39;</span><span class="p">,</span> <span class="c1">// region字段,极其有必要加上!我在sts场景,不加region字段就报错权限不够/心累... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">endPoint</span><span class="o">:</span> <span class="s1">&#39;192.168.1.1&#39;</span><span class="p">,</span> <span class="c1">// minio的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">port</span><span class="o">:</span> <span class="mi">9000</span><span class="p">,</span> <span class="c1">// minio端口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">useSSL</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 是否使用ssl +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">accessKey</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">secretKey</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sessionToken</span><span class="p">,</span> <span class="c1">// 可选字段,当为sts时,加入此字段 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="putobject直传方案">putObject直传方案</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">putObject</span><span class="p">(</span><span class="nx">bucketName</span><span class="p">,</span> <span class="nx">objectName</span><span class="p">,</span> <span class="nx">stream</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>官方提供的<code>putObject</code>方法,必填这三个字段,前两个很好理解,主要是第三个<code>stream</code>,需要详细看一下。</p> +<p><code>stream</code>的类型是:<code>string | internal.Readable | Buffer</code></p> +<p><code>string</code>很好搞定,直接<code>putObject(bucketName, 'hello.txt', 'hello,world!')</code>这样子上传文本文件。</p> +<p>但是另外两个类型都是nodejs的啊&hellip;啊这&hellip;(也许是我错了,有大佬能够解决的请务必直接告诉我&hellip;)</p> +<h3 id="折中方案">折中方案</h3> +<p>那么折中的方案就是由前端生成临时签名url,再由前端进行上传 /哭。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">minioClient</span><span class="p">.</span><span class="nx">presignedPutObject</span><span class="p">(</span><span class="nx">bucketName</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后利用http请求url进行上传即可。虽然比较曲折,但是目前相对比较好的前端解决方案。</p> +<h2 id="http请求url上传">http请求url上传</h2> +<p>此处介绍下http请求上传的相关操作。</p> +<h3 id="请求上传">请求上传</h3> +<p>请求上传可以有三种方案。</p> +<h4 id="xmlhttprequest">XMLHttpRequest</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">xhrUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="fetch">Fetch</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fetchUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;PUT&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">body</span><span class="o">:</span> <span class="nx">file</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="axios">Axios</h4> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">axiosUploadFile</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">file</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="nx">file</span><span class="p">.</span><span class="nx">type</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传成功`</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> 上传失败`</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="promise">Promise</h3> +<p>此处可以封装一下请求,叠加<code>promise</code>buff,此处以<code>XMLHttpRequest</code>为例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">uploadRequest</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">response</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> +</span></span><span class="line"><span class="cl"> <span class="nx">reject</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="事件响应">事件响应</h3> +<p>要完成上传,怎么能没有响应事件呢。</p> +<p>此处的事件包括:</p> +<ul> +<li>上传进度</li> +<li>上传完成</li> +<li>上传失败</li> +</ul> +<p>请求代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">uploadRequest</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">upload</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;progress&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploadProgress</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="p">((</span><span class="nx">e</span><span class="p">.</span><span class="nx">loaded</span> <span class="o">/</span> <span class="nx">e</span><span class="p">.</span><span class="nx">total</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">).</span><span class="nx">toFixed</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> <span class="c1">// 更新进度,此处不保留小数点 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploaded</span><span class="p">()</span> <span class="c1">// 响应上传完成事件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onUploadErr</span><span class="p">((</span><span class="nx">error</span> <span class="nx">as</span> <span class="nb">Error</span><span class="p">).</span><span class="nx">message</span><span class="p">)</span> <span class="c1">// 响应上传错误,此处ts处理error +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> <span class="nx">onUploadErr</span><span class="p">(</span><span class="sb">`http code is </span><span class="si">${</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> <span class="p">}</span> <span class="c1">// 响应上传错误 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;PUT&#39;</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="取消上传">取消上传</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">xhr</span><span class="p">.</span><span class="nx">abort</span><span class="p">()</span> <span class="c1">// 取消上传 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="性能优化">性能优化</h2> +<ul> +<li>对于大文件,或者说需要后台运行的上传任务,可以使用web worker来跑</li> +<li>对于大文件,可以使用切片的方式提高并发,切片的方式也可以实现断点续传,只是需要注意文件切片的顺序和唯一id</li> +</ul> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/minio/minio-js" target="_blank" rel="noopener" + >github.com/minio/minio-js</a></li> +</ul> + + + + VUE3+TS+微前端实践 + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + Wed, 16 Feb 2022 15:07:56 +0000 + + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + <h2 id="前言">前言</h2> +<p>基于架构的调整,前端开始转为微前端。经过调研,决定使用<a class="link" href="https://qiankun.umijs.org/zh/guide/getting-started" target="_blank" rel="noopener" + >qiankun</a>微服务框架来使用,本文将介绍VUE3+TS+qiankun的实践经过。微服务架构的优势之一在于可以结合不同技术栈的节点,基于技术栈的考虑,此处用的都是vue3。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>vue 3.0.0</li> +<li>TypeScript 4.1.5</li> +<li>vue router 4.0.0</li> +<li>@vue/cli 4.5.15</li> +<li>qiankun 2.6.3</li> +</ul> +<h2 id="实践">实践</h2> +<h3 id="架构">架构</h3> +<p><img src="https://blog.hunterji.com/../assets/qiankun_example.jpg" + + + + loading="lazy" + + alt="qiankun_example" + + +></p> +<p>如上图所示,微服务架构将会由多个节点构成,首先由一个主节点<code>site_base</code>连接所有子节点,子节点可以不断拓展。</p> +<h3 id="主节点">主节点</h3> +<p>主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base</a></p> +<p>创建主节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_base +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_base +</span></span></code></pre></td></tr></table> +</div> +</div><p>安装<code>qiankun</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add qiankun +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/App.vue</code>中添加路由和渲染节点</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;nav&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/about&#34;</span><span class="p">&gt;</span>About<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site1&#34;</span><span class="p">&gt;</span>Site1<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site2&#34;</span><span class="p">&gt;</span>Site2<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span><span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site1&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site2&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/main.ts</code>中引入子节点配置</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id,对应上一步中src/App.vue中的渲染节点 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9002/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site2&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> <span class="c1">// 注册应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="子节点">子节点</h3> +<p>子节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1</a></p> +<p>此处以<code>site1</code>为例,<code>site2</code>同理。</p> +<p>创建子节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_1 +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_1 +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/App.vue</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/views/Home.vue</code>,修改其内容,写一点标识性的文本</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Hello, Site1!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/pulic-path.ts</code>,第一行的注视一定要加,避免eslint对于变量的报错</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="cm">/* eslint-disable camelcase */</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">((</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">__webpack_public_path__</span> <span class="o">=</span> <span class="p">(</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__INJECTED_PUBLIC_PATH_BY_QIANKUN__</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/router/index.ts</code>,此处直接返回<code>routes</code>,而不是<code>router</code>,并且修改</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">RouteRecordRaw</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">RouteRecordRaw</span><span class="p">&gt;</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Home&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/Home.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/about&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;About&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/About.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 直接返回routes,由其它地方处理创建路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">routes</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;./public-path&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createRouter</span><span class="p">,</span> <span class="nx">createWebHistory</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">routes</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">instance</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">history</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">render</span> <span class="p">(</span><span class="nx">props</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="p">{</span> <span class="nx">container</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">props</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当为微服务主节点情况下访问,会设置二级路径,而直接访问时没有二级路径,此处需要根据实际情况修改 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">history</span> <span class="o">=</span> <span class="nx">createWebHistory</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span> <span class="o">?</span> <span class="s1">&#39;/site1&#39;</span> <span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="nx">createRouter</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">routes</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">mount</span><span class="p">(</span><span class="nx">container</span> <span class="o">?</span> <span class="nx">container</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> <span class="o">:</span> <span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">bootstrap</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;%c &#39;</span><span class="p">,</span> <span class="s1">&#39;color: green &#39;</span><span class="p">,</span> <span class="s1">&#39;vue3.0 app bootstraped&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">storeTest</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="k">void</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`[onGlobalStateChange - </span><span class="si">${</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">]:`</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ignore</span>: <span class="kt">props.name</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span>: <span class="kt">props.name</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">mount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">storeTest</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$onGlobalStateChange</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$setGlobalState</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">unmount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">unmount</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">_container</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">.</span><span class="nx">destroy</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>vue.config.js</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">name</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./package&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">resolve</span> <span class="p">(</span><span class="nx">dir</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="nx">dir</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">port</span> <span class="o">=</span> <span class="mi">9001</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputDir</span><span class="o">:</span> <span class="s1">&#39;dist&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assetsDir</span><span class="o">:</span> <span class="s1">&#39;static&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filenameHashing</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">devServer</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">hot</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">disableHostCheck</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">overlay</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">warnings</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">errors</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Access-Control-Allow-Origin&#39;</span><span class="o">:</span> <span class="s1">&#39;*&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 自定义webpack配置 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">configureWebpack</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alias</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;@&#39;</span><span class="o">:</span> <span class="nx">resolve</span><span class="p">(</span><span class="s1">&#39;src&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">output</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 把子应用打包成 umd 库格式 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">library</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">-[name]`</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">libraryTarget</span><span class="o">:</span> <span class="s1">&#39;umd&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonpFunction</span><span class="o">:</span> <span class="sb">`webpackJsonp_</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证">验证</h3> +<p>主节点和子节点分别独立运行,但是子节点的地址需要跟主节点配置中子节点对应的地址相同。</p> +<p>在主节点上点击子节点的路由,即可在主节点上访问子节点的页面了!</p> +<img src="../assets/qiangkun_example_result.png" alt="qiangkun_example_result" style="zoom:50%;" /> +<h2 id="主节点优化">主节点优化</h2> +<p>主节点除了如上配置,可以进行两项优化:</p> +<ul> +<li>模块化子节点配置</li> +<li>添加过渡状态,当加载子节点时窗口顶部出现加载进度条</li> +</ul> +<p>优化后主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize</a></p> +<h3 id="模块化子节点配置">模块化子节点配置</h3> +<p>创建文件夹<code>src/childNodes</code>,然后创建文件<code>src/childNodes/apps.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">apps</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">start</span> <span class="kr">from</span> <span class="s1">&#39;./childNodes&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="过渡效果">过渡效果</h3> +<p>此处的过渡效果采用<code>NProgress</code>库,先来安装一波</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add nprogress +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addGlobalUncaughtErrorHandler</span><span class="p">,</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">NProgress</span> <span class="kr">from</span> <span class="s1">&#39;nprogress&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;nprogress/nprogress.css&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点加载前 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">beforeLoad</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开始进度条 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点挂载后 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">afterMount</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">done</span><span class="p">()</span> <span class="c1">// 进度条结束 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p><code>qiankun</code>框架确实挺不错的,配置也并不是复杂,但是唯一想吐槽的一点是对于ts的支持感觉不太好/狗头,或许是我写得不够好吧,后面会持续优化使用。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://qiankun.umijs.org/zh/guide" target="_blank" rel="noopener" + >qiankun官方文档</a></li> +<li><a class="link" href="https://juejin.cn/post/6981656757458173988" target="_blank" rel="noopener" + >vue3+ts+qiankun的微前端快速上手</a></li> +</ul> + + + + vue3+ts+electron不支持require is not defined报错解决 + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:23:46 +0000 + + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue3+typescript+electron开发时,遇到一个报错为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Uncaught ReferenceError: require is not defined +</span></span></code></pre></td></tr></table> +</div> +</div><p>点进去是<code>module.exports = require(&quot;events&quot;)</code>,并不是自己的代码中的<code>require</code>,因此无法改变写法只能让项目去支持它。</p> +<h2 id="解决">解决</h2> +<p>在electron的配置文件中,新增或者修改如下配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">contextIsolation</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/64706829/electron-tedious-requireevents-is-not-defined" target="_blank" rel="noopener" + >Electron/Tedious: require(&ldquo;events&rdquo;) is not defined</a></li> +</ul> + + + + + diff --git a/tags/typescript/page/1/index.html b/tags/typescript/page/1/index.html new file mode 100644 index 0000000..b181872 --- /dev/null +++ b/tags/typescript/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/typescript/ + + + + + + diff --git a/tags/uniapp/index.html b/tags/uniapp/index.html new file mode 100644 index 0000000..4213e88 --- /dev/null +++ b/tags/uniapp/index.html @@ -0,0 +1,537 @@ + + + + +Tag: uniapp - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

uniapp

+ +
+
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git a/tags/uniapp/index.xml b/tags/uniapp/index.xml new file mode 100644 index 0000000..e1ec936 --- /dev/null +++ b/tags/uniapp/index.xml @@ -0,0 +1,475 @@ + + + + uniapp on 开发者小橙 + https://blog.hunterji.com/tags/uniapp/ + Recent content in uniapp on 开发者小橙 + Hugo -- gohugo.io + en-us + Thu, 25 Nov 2021 17:07:55 +0000 + uniapp canvas生成海报功能拆解和问题记录 + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + Thu, 25 Nov 2021 17:07:55 +0000 + + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + <h2 id="前言">前言</h2> +<p>最近在用uniapp开发小程序,需要用到canvas画海报然后再保存本地。</p> +<p>之前写过同样功能的<a class="link" href="https://github.com/Kuari/Blog/issues/1" target="_blank" rel="noopener" + >文章</a>,不过场景不同,之前是在web上生成海报,该场景可以使用之前文章的方法——html转canvas来实现。</p> +<p>但是uniapp则不同,该框架是去DOM化的,因此只能使用uniapp的官方canvas来实现。</p> +<h2 id="功能拆解">功能拆解</h2> +<p>网上找到的文章,有几篇写得挺好的,展现了完整的功能。这里我把用到的几个功能拆解出来,而不用先通读整篇代码。</p> +<h3 id="创建canvas">创建canvas</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">canvas</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width: 300px; height: 200px;&#34;</span> <span class="na">canvas-id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">canvas</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onReady</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="背景色">背景色</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;red&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 此处其实绘制了一个300x200的红色矩形,但是其大小跟canvas大小相同即为背景色了 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="加载图片">加载图片</h3> +<h4 id="1本地图片">1)本地图片</h4> +<p>图片直接在项目中,可以直接加载。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="s2">&#34;./background.png&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="2url">2)url</h4> +<p>此处url返回的为文件流,在uniapp中无法直接加载,需要转换成本地信息才可以使用。</p> +<p>有两种方式可以使用,小程序都需要添加download合法域名:</p> +<ul> +<li><em><strong>uni.getImageInfo</strong></em>:获取文件信息,我使用的这个方法</li> +<li><em><strong>uni.downloadFile</strong></em>:下载文件</li> +</ul> +<p>原生使用方法是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>由于js的异步问题,如果图片较大或者多个图片的情况下,会有这边还没加载完,canvas就已经绘制完了的情况,所以这里将其优化下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 首先封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">url</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3base64">3)base64</h4> +<p>如果你的图片数据是base64的,那恭喜你,依然加载不了。当然这存在的情况是,微信开发工具是没有问题的,但是上了真机之后直接无法加载了,这波是小程序的锅。</p> +<p>这里呢需要将图片存储然后用本地地址绘制。</p> +<p>原生方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>优化一波:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getBase64ImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">base64Data</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="nx">base64Data</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getBase64ImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="文字">文字</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFontSize</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">font</span> <span class="o">=</span> <span class="s2">&#34;nomarl bold 13px Arial,sans-serif&#34;</span> <span class="c1">// 加粗等功能 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillText</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="圆角矩形">圆角矩形</h3> +<p>想要绘制一个圆角的矩形,啊&hellip;&hellip;这波就复杂了,原理就不细讲了,直接上代码,调用即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">roundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">width</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">height</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">drawRoundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">strokeStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">fillStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">roundedRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">strokeStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">fillStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">drawRoundedRect</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">86</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="图片加载为圆形">图片加载为圆形</h3> +<p>基本原理是,正常加载图片,canvas画个圆给它裁剪掉,上代码!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 如果小伙伴儿调试时候感觉圆形和图片有点错位,可以开启下面两行注释代码,给圆圈加个边框 +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.setStrokeStyle(&#39;#AAAAAA&#39;) +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.stroke() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">clip</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">image</span><span class="o">-</span><span class="nx">path</span><span class="o">&gt;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">restore</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas生成的海报下载">canvas生成的海报下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">savePoster</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;确定保存到相册吗&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">canvasToTempFilePath</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvasId</span><span class="o">:</span> <span class="s1">&#39;sharePoster&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">saveImageToPhotosAlbum</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">tempFilePath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 此处为执行成功 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">openSetting</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">authSetting</span><span class="p">[</span><span class="s1">&#39;scope.writePhotosAlbum&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限成功,再次点击图片即可保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限失败,无法保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="k">this</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="问题">问题</h2> +<h3 id="图片有时显示有时不显示">图片有时显示有时不显示</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.加载图片”优化代码处,将加载图片全部写成同步的,在开始绘制前将图片全都加载好。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="base64数据的图片在小程序开发工具显示到了真机就不显示了">base64数据的图片在小程序开发工具显示,到了真机就不显示了</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.3)base64”优化代码处,使用该方法即可。小程序canvas无法直接加载base64图片。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas整体画成圆角的">canvas整体画成圆角的</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">canvas背景是透明色,只要画个大小覆盖canvas的圆角矩形或者使用圆角背景图即可。 +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/uniapp/page/1/index.html b/tags/uniapp/page/1/index.html new file mode 100644 index 0000000..b9e0c15 --- /dev/null +++ b/tags/uniapp/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/uniapp/ + + + + + + diff --git a/tags/vue/index.html b/tags/vue/index.html new file mode 100644 index 0000000..c9f739d --- /dev/null +++ b/tags/vue/index.html @@ -0,0 +1,673 @@ + + + + +Tag: Vue - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

14 pages

+

Vue

+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + diff --git a/tags/vue/index.xml b/tags/vue/index.xml new file mode 100644 index 0000000..27d2cef --- /dev/null +++ b/tags/vue/index.xml @@ -0,0 +1,3869 @@ + + + + Vue on 开发者小橙 + https://blog.hunterji.com/tags/vue/ + Recent content in Vue on 开发者小橙 + Hugo -- gohugo.io + en-us + Sat, 28 Jan 2023 16:09:44 +0000 + wow.js和animate css在vue3中的应用 + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + Sat, 28 Jan 2023 16:09:44 +0000 + + https://blog.hunterji.com/p/wow.js%E5%92%8Canimate-css%E5%9C%A8vue3%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/ + <h2 id="环境">环境</h2> +<ul> +<li>vue 3.2</li> +<li>typescript 4.7.4</li> +<li>wow.js 1.2.2</li> +<li>animate.css 4.1.1</li> +</ul> +<h2 id="animatecss">animate.css</h2> +<h3 id="下载">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add animate.css -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;animate.css&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<p>需要注意的是,animate css在4.0之后使用<code>animate__</code>前缀</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="动画延迟">动画延迟</h3> +<h4 id="官方方法">官方方法</h4> +<p>官方给出的动画延迟是<code>animate__delay-2s</code>、<code>animate__delay-3s</code> &hellip;&hellip;</p> +<p>直接在class中添加即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animate__delay-2s&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="自定义延迟">自定义延迟</h4> +<p>特殊场景需要使用不同于官方的延迟时间,因此可以自定义延迟时间,直接声明延迟的类,然后在class上加入即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-1</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">100</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-2</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">300</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-3</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">500</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-4</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">700</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">animation-delay-5</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">animation-delay</span><span class="p">:</span> <span class="mi">900</span><span class="kt">ms</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;animate__animated animate__bounce animation-delay-2&#34;</span><span class="p">&gt;</span>Another animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="wowjs">wow.js</h2> +<h3 id="下载-1">下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pnpm add wow.js -D +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<p>在<code>vue3</code>项目的<code>main.ts</code>中引入,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">WOW</span> <span class="nx">from</span> <span class="s1">&#39;wow.js&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">WOW</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">boxClass</span><span class="o">:</span> <span class="s1">&#39;wow&#39;</span><span class="p">,</span> <span class="c1">// 类名,在用户滚动时显示隐藏的框。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">animateClass</span><span class="o">:</span> <span class="s1">&#39;animate__animated&#39;</span><span class="p">,</span> <span class="c1">// 触发CSS动画的类名称 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">offset</span><span class="o">:</span> <span class="mi">300</span><span class="p">,</span> <span class="c1">// 定义浏览器视口底部与隐藏框顶部之间的距离。当用户滚动并到达此距离时,隐藏的框会显示出来。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mobile</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在移动设备上打开/关闭WOW.js。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">live</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// 在页面上同时检查新的WOW元素。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}).</span><span class="nx">init</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用-1">使用</h3> +<p>使用<code>wow</code>直接替代<code>animate__animated</code>即可</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;wow animate__bounce animation-delay-1&#34;</span><span class="p">&gt;</span>An animated element<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p>由于写一个页面需要使用到wow,好多年没用过了,查了一下文档超多版本教程,使用起来各种不成功,难受&hellip;暂时也没找到可替代的方案&hellip;</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://github.com/graingert/wow" target="_blank" rel="noopener" + >wow</a></li> +<li><a class="link" href="https://animate.style/" target="_blank" rel="noopener" + >animate css</a></li> +</ul> + + + + VUE3+TS+微前端实践 + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + Wed, 16 Feb 2022 15:07:56 +0000 + + https://blog.hunterji.com/p/vue3-ts-%E5%BE%AE%E5%89%8D%E7%AB%AF%E5%AE%9E%E8%B7%B5/ + <h2 id="前言">前言</h2> +<p>基于架构的调整,前端开始转为微前端。经过调研,决定使用<a class="link" href="https://qiankun.umijs.org/zh/guide/getting-started" target="_blank" rel="noopener" + >qiankun</a>微服务框架来使用,本文将介绍VUE3+TS+qiankun的实践经过。微服务架构的优势之一在于可以结合不同技术栈的节点,基于技术栈的考虑,此处用的都是vue3。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>vue 3.0.0</li> +<li>TypeScript 4.1.5</li> +<li>vue router 4.0.0</li> +<li>@vue/cli 4.5.15</li> +<li>qiankun 2.6.3</li> +</ul> +<h2 id="实践">实践</h2> +<h3 id="架构">架构</h3> +<p><img src="https://blog.hunterji.com/../assets/qiankun_example.jpg" + + + + loading="lazy" + + alt="qiankun_example" + + +></p> +<p>如上图所示,微服务架构将会由多个节点构成,首先由一个主节点<code>site_base</code>连接所有子节点,子节点可以不断拓展。</p> +<h3 id="主节点">主节点</h3> +<p>主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base</a></p> +<p>创建主节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_base +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_base +</span></span></code></pre></td></tr></table> +</div> +</div><p>安装<code>qiankun</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add qiankun +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/App.vue</code>中添加路由和渲染节点</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;nav&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/about&#34;</span><span class="p">&gt;</span>About<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site1&#34;</span><span class="p">&gt;</span>Site1<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> | +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2路由 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/site2&#34;</span><span class="p">&gt;</span>Site2<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span><span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site1渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site1&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 新增site2渲染节点 --&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site2&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在<code>src/main.ts</code>中引入子节点配置</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id,对应上一步中src/App.vue中的渲染节点 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9002/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site2&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site2&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> <span class="c1">// 注册应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="子节点">子节点</h3> +<p>子节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_1</a></p> +<p>此处以<code>site1</code>为例,<code>site2</code>同理。</p> +<p>创建子节点,选择vue3+ts</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue create site_1 +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> site_1 +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/App.vue</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">router-view</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/views/Home.vue</code>,修改其内容,写一点标识性的文本</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Hello, Site1!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/pulic-path.ts</code>,第一行的注视一定要加,避免eslint对于变量的报错</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="cm">/* eslint-disable camelcase */</span> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">((</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">__webpack_public_path__</span> <span class="o">=</span> <span class="p">(</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__INJECTED_PUBLIC_PATH_BY_QIANKUN__</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/router/index.ts</code>,此处直接返回<code>routes</code>,而不是<code>router</code>,并且修改</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">RouteRecordRaw</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">RouteRecordRaw</span><span class="p">&gt;</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Home&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/Home.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/about&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;About&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;../views/About.vue&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 直接返回routes,由其它地方处理创建路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">routes</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;./public-path&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createRouter</span><span class="p">,</span> <span class="nx">createWebHistory</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue-router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">routes</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">instance</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">history</span>: <span class="kt">any</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">render</span> <span class="p">(</span><span class="nx">props</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="p">{</span> <span class="nx">container</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">props</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 当为微服务主节点情况下访问,会设置二级路径,而直接访问时没有二级路径,此处需要根据实际情况修改 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">history</span> <span class="o">=</span> <span class="nx">createWebHistory</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span> <span class="o">?</span> <span class="s1">&#39;/site1&#39;</span> <span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="nx">createRouter</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">routes</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">mount</span><span class="p">(</span><span class="nx">container</span> <span class="o">?</span> <span class="nx">container</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> <span class="o">:</span> <span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">window</span><span class="p">.</span><span class="nx">__POWERED_BY_QIANKUN__</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">bootstrap</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;%c &#39;</span><span class="p">,</span> <span class="s1">&#39;color: green &#39;</span><span class="p">,</span> <span class="s1">&#39;vue3.0 app bootstraped&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">storeTest</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="k">void</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`[onGlobalStateChange - </span><span class="si">${</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">]:`</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">prev</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> <span class="o">&amp;&amp;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ignore</span>: <span class="kt">props.name</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span>: <span class="kt">props.name</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">mount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">any</span><span class="p">)</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">storeTest</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">render</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$onGlobalStateChange</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">onGlobalStateChange</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">globalProperties</span><span class="p">.</span><span class="nx">$setGlobalState</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">setGlobalState</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">unmount</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">void</span><span class="p">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">unmount</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span><span class="p">.</span><span class="nx">_container</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">instance</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">router</span> <span class="o">=</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"> <span class="nx">history</span><span class="p">.</span><span class="nx">destroy</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>vue.config.js</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">name</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./package&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">resolve</span> <span class="p">(</span><span class="nx">dir</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="nx">dir</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">port</span> <span class="o">=</span> <span class="mi">9001</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputDir</span><span class="o">:</span> <span class="s1">&#39;dist&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">assetsDir</span><span class="o">:</span> <span class="s1">&#39;static&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filenameHashing</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">devServer</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">hot</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">disableHostCheck</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">port</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">overlay</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">warnings</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">errors</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Access-Control-Allow-Origin&#39;</span><span class="o">:</span> <span class="s1">&#39;*&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 自定义webpack配置 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">configureWebpack</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">alias</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s1">&#39;@&#39;</span><span class="o">:</span> <span class="nx">resolve</span><span class="p">(</span><span class="s1">&#39;src&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">output</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 把子应用打包成 umd 库格式 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">library</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">-[name]`</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">libraryTarget</span><span class="o">:</span> <span class="s1">&#39;umd&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsonpFunction</span><span class="o">:</span> <span class="sb">`webpackJsonp_</span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证">验证</h3> +<p>主节点和子节点分别独立运行,但是子节点的地址需要跟主节点配置中子节点对应的地址相同。</p> +<p>在主节点上点击子节点的路由,即可在主节点上访问子节点的页面了!</p> +<img src="../assets/qiangkun_example_result.png" alt="qiangkun_example_result" style="zoom:50%;" /> +<h2 id="主节点优化">主节点优化</h2> +<p>主节点除了如上配置,可以进行两项优化:</p> +<ul> +<li>模块化子节点配置</li> +<li>添加过渡状态,当加载子节点时窗口顶部出现加载进度条</li> +</ul> +<p>优化后主节点源码可见于<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/microFrontend/site_base_optimize</a></p> +<h3 id="模块化子节点配置">模块化子节点配置</h3> +<p>创建文件夹<code>src/childNodes</code>,然后创建文件<code>src/childNodes/apps.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">apps</span>: <span class="kt">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;site1&#39;</span><span class="p">,</span> <span class="c1">// 应用的名字 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">entry</span><span class="o">:</span> <span class="s1">&#39;http://localhost:9001/&#39;</span><span class="p">,</span> <span class="c1">// 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">container</span><span class="o">:</span> <span class="s1">&#39;#site1&#39;</span><span class="p">,</span> <span class="c1">// 要渲染到的节点id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">activeRule</span><span class="o">:</span> <span class="s1">&#39;/site1&#39;</span> <span class="c1">// 访问子节点路由 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">apps</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>创建文件<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/main.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="kr">from</span> <span class="s1">&#39;./App.vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="kr">from</span> <span class="s1">&#39;./router&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="kr">from</span> <span class="s1">&#39;./store&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">start</span> <span class="kr">from</span> <span class="s1">&#39;./childNodes&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开启应用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">store</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="过渡效果">过渡效果</h3> +<p>此处的过渡效果采用<code>NProgress</code>库,先来安装一波</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add nprogress +</span></span></code></pre></td></tr></table> +</div> +</div><p>编辑<code>src/childNodes/index.ts</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addGlobalUncaughtErrorHandler</span><span class="p">,</span> <span class="nx">registerMicroApps</span><span class="p">,</span> <span class="nx">start</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;qiankun&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">apps</span> <span class="kr">from</span> <span class="s1">&#39;./apps&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">NProgress</span> <span class="kr">from</span> <span class="s1">&#39;nprogress&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;nprogress/nprogress.css&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">registerMicroApps</span><span class="p">(</span><span class="nx">apps</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点加载前 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">beforeLoad</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">start</span><span class="p">()</span> <span class="c1">// 开始进度条 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// qiankun 生命周期钩子 - 子节点挂载后 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">afterMount</span><span class="o">:</span> <span class="p">(</span><span class="nx">app</span>: <span class="kt">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NProgress</span><span class="p">.</span><span class="nx">done</span><span class="p">()</span> <span class="c1">// 进度条结束 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">start</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="结语">结语</h2> +<p><code>qiankun</code>框架确实挺不错的,配置也并不是复杂,但是唯一想吐槽的一点是对于ts的支持感觉不太好/狗头,或许是我写得不够好吧,后面会持续优化使用。</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://qiankun.umijs.org/zh/guide" target="_blank" rel="noopener" + >qiankun官方文档</a></li> +<li><a class="link" href="https://juejin.cn/post/6981656757458173988" target="_blank" rel="noopener" + >vue3+ts+qiankun的微前端快速上手</a></li> +</ul> + + + + VUE项目国际化 + https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/ + Wed, 29 Dec 2021 14:24:30 +0000 + + https://blog.hunterji.com/p/vue%E9%A1%B9%E7%9B%AE%E5%9B%BD%E9%99%85%E5%8C%96/ + <h2 id="前言">前言</h2> +<p><code>i18n</code>是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。 在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。</p> +<p>Node.js本身有一个<code>i18n</code>的包,但是为了更好地结合vue,此处我们使用的是<code>vue-i18n</code>。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vue_i18n_demo</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Vue 3.0.0</li> +<li>TypeScript 4.5.4</li> +<li>Vue Cli 4.5.15</li> +</ul> +<h2 id="安装">安装</h2> +<p>使用vue cli安装,会自动生成文件且引入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vue add i18n +</span></span></code></pre></td></tr></table> +</div> +</div><p>执行过程中将需要填写如下问题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">locale</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="w"> </span><span class="n">localization</span><span class="p">.</span><span class="w"> </span><span class="p">[</span><span class="err">默认选项</span><span class="p">]</span><span class="n">en</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">fallback</span><span class="w"> </span><span class="n">locale</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="w"> </span><span class="n">localization</span><span class="p">.</span><span class="w"> </span><span class="p">[</span><span class="err">默认选项</span><span class="p">]</span><span class="n">en</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">?</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">directory</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">store</span><span class="w"> </span><span class="n">localization</span><span class="w"> </span><span class="n">messages</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">project</span><span class="p">.</span><span class="w"> </span><span class="n">It</span><span class="s1">&#39;s stored under `src` directory. [默认选项]locales +</span></span></span><span class="line"><span class="cl"><span class="s1">? Enable legacy API (compatible vue-i18n@v8.x) mode ? [默认选项]No +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>执行完成之后,将会自动处理如下文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// vue add i18n执行结束后的git status +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">new file: .env // 执行过程中的第一个和第二个问题将使用环境变量更新 +</span></span><span class="line"><span class="cl">modified: package.json +</span></span><span class="line"><span class="cl">new file: src/components/HelloI18n.vue // 官方给出的demo组件 +</span></span><span class="line"><span class="cl">new file: src/i18n.ts // 自动读取多语言的json文件且创建i18n实例 +</span></span><span class="line"><span class="cl">new file: src/locales/en.json // 多语言json文件所在文件夹,默认选择的en语言,所以生成一个默认的语言json文件 +</span></span><span class="line"><span class="cl">modified: src/main.ts // 引入i18n +</span></span><span class="line"><span class="cl">new file: vue.config.js // 配置i18n +</span></span><span class="line"><span class="cl">modified: yarn.lock +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="多语言配置">多语言配置</h2> +<p>想要多语言则需要配置多个对应语言的json文件,其字段必须相同,否则当用户切换时,会出现找不到该字段对应文字的问题。</p> +<p>为了便于切换和处理,此处不再使用<code>en</code>之类的简写,而是和系统语言名称对应起来,此处将使用<code>zh_CN</code>和<code>en_GB</code>来做配置。</p> +<h3 id="创建多语言json文件">创建多语言json文件</h3> +<p>此处生成两个文件<code>src/locales/zh_CN.json</code>和<code>src/locales/en_GB.json</code>,内容分别如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;中文&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;你好,世界!&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;English&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello, World !&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="更新语言标识">更新语言标识</h3> +<p>将相关文件的<code>en</code>都改成<code>en_GB</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">##env +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">VUE_APP_I18N_LOCALE=en_GB +</span></span><span class="line"><span class="cl">VUE_APP_I18N_FALLBACK_LOCALE=en_GB +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/i18n.ts +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">createI18n</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">legacy</span>: <span class="kt">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span>: <span class="kt">process.env.VUE_APP_I18N_LOCALE</span> <span class="o">||</span> <span class="s1">&#39;en_GB&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fallbackLocale</span>: <span class="kt">process.env.VUE_APP_I18N_FALLBACK_LOCALE</span> <span class="o">||</span> <span class="s1">&#39;en_GB&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">messages</span>: <span class="kt">loadLocaleMessages</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<h3 id="重写首页">重写首页</h3> +<p>此处重写<code>src/views/Home.vue</code>文件,先按照正常的内容去写,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nx">Hello</span><span class="p">,</span> <span class="nx">World</span> <span class="o">!</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置多语言">配置多语言</h3> +<p>需要配置多语言的地方就是展示的文字,所以将该文字替换掉即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useI18n</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue-i18n&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">t</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useI18n</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处引入<code>t</code>方法,而其参数<code>message</code>为上面配置的每一个json文件中的<code>message</code>字段,其将用json对应字段的value来展示。</p> +<p>可能有小伙伴儿要问,那如果不是html中的展示文字怎么办呢?</p> +<p>这也一样使用<code>t</code>方法的,示例如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="na">:placeholder</span><span class="o">=</span><span class="s">&#34;t(&#39;message&#39;)&#34;</span> <span class="p">/&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useI18n</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue-i18n&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">t</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useI18n</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">exampleVal</span> <span class="o">=</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处我们的json结构都是单层的,如果是嵌套的结构的话,可以使用<code>t('home.message')</code>这样的方式来引用。</p> +<h3 id="切换语言">切换语言</h3> +<p>此处需要引入<code>locale</code>,主要通过更新locale.value来切换语言。此处的切换是全局的,所以只需要写一个切换组件,其它组件都将会被切换语言。</p> +<p>写一个下拉框来实现语言的切换,完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">select</span> <span class="nt">v-model</span><span class="s">=&#34;state.language&#34; @change=&#34;handleLanguageChange&#34;&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;zh_CN&#34;&gt;zh_CN&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;en_GB&#34;&gt;en_GB&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/select&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/div&gt; +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;/template&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;script setup lang=&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">reactive</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">useI18n</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="nt">-i18n</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="p">{</span> <span class="na">t</span><span class="err">,</span> <span class="na">locale</span> <span class="p">}</span> <span class="err">=</span> <span class="na">useI18n</span><span class="err">()</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">state</span><span class="o">:</span> <span class="p">{</span> <span class="na">language</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="err">=</span> <span class="na">reactive</span><span class="err">(</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="na">language</span><span class="o">:</span> <span class="err">&#39;</span><span class="na">en_GB</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">)</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">handleLanguageChange </span><span class="o">=</span> <span class="err">(</span><span class="na">option</span><span class="o">:</span> <span class="p">{</span> <span class="na">target</span><span class="o">:</span> <span class="p">{</span> <span class="na">value</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="p">}</span><span class="err">)</span> <span class="err">=</span><span class="p">&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 主要通过更新locale.value来切换语言 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">option</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>通过下拉框切换选项可以切换语言。</p> +<h3 id="本地环境">本地环境</h3> +<p>虽然<code>i18n</code>设置了默认的语言,但是友好的交互应当是根据用户的环境语言加载。这里需要使用<code>navigator.language</code>来拿到用户的环境语言,通过判断环境语言切换初始的语言配置。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">language</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;zh-CN&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;en-GB&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>完整代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{{</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">)</span> <span class="p">}}&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">select</span> <span class="nt">v-model</span><span class="s">=&#34;state.language&#34; @change=&#34;handleLanguageChange&#34;&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;zh_CN&#34;&gt;zh_CN&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;option value=&#34;en_GB&#34;&gt;en_GB&lt;/option&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/select&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/div&gt; +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;/template&gt; +</span></span></span><span class="line"><span class="cl"><span class="s"> +</span></span></span><span class="line"><span class="cl"><span class="s">&lt;script setup lang=&#34;ts&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">reactive</span><span class="err">,</span> <span class="na">onMounted</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="na">import</span> <span class="p">{</span> <span class="na">useI18n</span> <span class="p">}</span> <span class="na">from</span> <span class="err">&#39;</span><span class="na">vue</span><span class="nt">-i18n</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="p">{</span> <span class="na">t</span><span class="err">,</span> <span class="na">locale</span> <span class="p">}</span> <span class="err">=</span> <span class="na">useI18n</span><span class="err">()</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">state</span><span class="o">:</span> <span class="p">{</span> <span class="na">language</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="err">=</span> <span class="na">reactive</span><span class="err">(</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="na">language</span><span class="o">:</span> <span class="err">&#39;</span><span class="na">en_GB</span><span class="err">&#39;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">)</span> +</span></span><span class="line"><span class="cl"><span class="na">const</span> <span class="na">handleLanguageChange </span><span class="o">=</span> <span class="err">(</span><span class="na">option</span><span class="o">:</span> <span class="p">{</span> <span class="na">target</span><span class="o">:</span> <span class="p">{</span> <span class="na">value</span><span class="o">:</span> <span class="na">string</span> <span class="p">}</span> <span class="p">}</span><span class="err">)</span> <span class="err">=</span><span class="p">&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 主要通过更新locale.value来切换语言 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">option</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fetchData</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">language</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 环境语言中间的线是居中的 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">case</span> <span class="s1">&#39;zh-CN&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;zh_CN&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s1">&#39;en-GB&#39;</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">break</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="nx">state</span><span class="p">.</span><span class="nx">language</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">locale</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;en_GB&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="nx">onMounted</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fetchData</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>运行之后,默认语言不再是英语,而是跟环境语言一致的。</p> +<h3 id="参考文档">参考文档</h3> +<ul> +<li><a class="link" href="https://kazupon.github.io/vue-i18n/zh/started.html" target="_blank" rel="noopener" + >vue-i18n</a></li> +</ul> + + + + vue3+ts+electron不支持require is not defined报错解决 + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + Mon, 29 Nov 2021 17:23:46 +0000 + + https://blog.hunterji.com/p/vue3-ts-electron%E4%B8%8D%E6%94%AF%E6%8C%81require-is-not-defined%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3/ + <h2 id="报错">报错</h2> +<p>在使用vue3+typescript+electron开发时,遇到一个报错为:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Uncaught ReferenceError: require is not defined +</span></span></code></pre></td></tr></table> +</div> +</div><p>点进去是<code>module.exports = require(&quot;events&quot;)</code>,并不是自己的代码中的<code>require</code>,因此无法改变写法只能让项目去支持它。</p> +<h2 id="解决">解决</h2> +<p>在electron的配置文件中,新增或者修改如下配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">contextIsolation</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">nodeIntegration</span><span class="o">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://stackoverflow.com/questions/64706829/electron-tedious-requireevents-is-not-defined" target="_blank" rel="noopener" + >Electron/Tedious: require(&ldquo;events&rdquo;) is not defined</a></li> +</ul> + + + + vue禁止遮罩层下的页面滚动 + https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/ + Thu, 25 Nov 2021 16:13:10 +0000 + + https://blog.hunterji.com/p/vue%E7%A6%81%E6%AD%A2%E9%81%AE%E7%BD%A9%E5%B1%82%E4%B8%8B%E7%9A%84%E9%A1%B5%E9%9D%A2%E6%BB%9A%E5%8A%A8/ + <h2 id="问题">问题</h2> +<p>功能开发过程中写遮罩时,遇到遮罩下页面还可以滚动的问题。</p> +<h2 id="解决">解决</h2> +<p>直接给遮罩下的元素套上一个样式,使其不可滚动。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">:class</span><span class="o">=</span><span class="s">&#34;isPopup ? &#39;disableRoll&#39; : &#39;&#39;&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> ... +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">isPopup</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nc">disableRoll</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">overflow</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue3 script setup响应式初体验 + https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/ + Thu, 04 Nov 2021 20:48:09 +0000 + + https://blog.hunterji.com/p/vue3-script-setup%E5%93%8D%E5%BA%94%E5%BC%8F%E5%88%9D%E4%BD%93%E9%AA%8C/ + <h2 id="前言">前言</h2> +<p>最近空下来,正好找个项目尝鲜,把vue3+ts+setup哐哐全堆上,试试最新的前端技术。</p> +<p>从最先体会到的变化,就是关于响应式APIs了。遇到不好问题,怪我没有理解文档/狗头。比如说:</p> +<ul> +<li>明明这个数据改了,怎么没渲染出来?</li> +<li>同样是Arrary,怎么套了个reactive就类型不一样了?</li> +</ul> +<p>所以这里基于遇到的几个问题,来写个笔记。</p> +<h2 id="简单对比">简单对比</h2> +<ul> +<li><a class="link" href="https://v3.cn.vuejs.org/api/sfc-script-setup.html#%E5%93%8D%E5%BA%94%E5%BC%8F" target="_blank" rel="noopener" + >响应式官方文档</a></li> +</ul> +<p>官方案例如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ref</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vue&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="nt">@click</span><span class="s">=&#34;count++&#34;</span><span class="p">&gt;{{</span> <span class="na">count</span> <span class="p">}}&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们可以先看看,如果是vue2,怎么做呢?</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">count</span><span class="o">:</span> <span class="mi">0</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">button</span> <span class="nt">@click</span><span class="s">=&#34;count++&#34;</span><span class="p">&gt;{{</span> <span class="na">count</span> <span class="p">}}&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以很明显地看到此处的<code>count</code>跟上面的官方文档不同,使用了<code>ref</code>方法。这就是setup中的响应式APIs,需要预先声明响应式变量。</p> +<p>如果,不声明呢?那就是直接写成如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>运行一下,首先你会发现,没有任何报错,代码正常运行。但是当你在浏览器上查看,开始改变<code>count</code>的值时,就会发现,怎么页面没有变化?</p> +<p>所以,需要手动命名响应,才会在值变化时触发视图渲染。</p> +<h2 id="ref">ref</h2> +<p>现在来详细讲讲<code>ref</code>,该方法常用于单个变量,比如:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ref</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;hello&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里需要说明一个问题,那就是<code>ref(&quot;hello&quot;) !== &quot;hello&quot;</code>。这就是我说的,为什么同样的值,类型就不同了。那是因为ref返回的是一个<code>Proxy</code>,而非原来的值。在视图中可直接使用,但是在js/ts中操作,需要使用<code>.value</code>来操作,如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;kuari&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">changeName</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;tom&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="reactive">reactive</h2> +<p><code>reactive</code>不同于<code>ref</code>的点在于,其是“深层”的——它影响所有嵌套 property。也就是说,其可用在对象或者数组上。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">form</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">desc</span><span class="o">:</span> <span class="s2">&#34;developer&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>其返回类型也是<code>Proxy</code>,不同点在于,可以直接修改某一个元素的,如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">form</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="s2">&#34;tom&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>但是如果你想整个替换就会报错了。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">form</span> <span class="o">=</span> <span class="p">{...}</span> <span class="c1">// 报错,类型不同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>当使用的是数组时,如果想整个替换,可以将其写成对象,代码如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">selected</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">arr</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;vue&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span>: <span class="kt">0</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;typescript&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">value</span>: <span class="kt">1</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">selected</span><span class="p">.</span><span class="nx">arr</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 整个替换 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">selected</span><span class="p">.</span><span class="nx">arr</span> <span class="o">=</span> <span class="p">[...]</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>关于<code>reactive</code>跟<code>ref</code>一起使用,<code>reactive</code> 将解包所有深层的<code>ref</code>,同时维持 ref 的响应性。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">reactive</span><span class="p">({</span> <span class="nx">count</span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// ref 会被解包 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span> <span class="o">===</span> <span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 它会更新 `obj.count` +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="o">++</span> +</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// 2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="p">)</span> <span class="c1">// 2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 它也会更新 `count` ref +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="o">++</span> +</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">count</span><span class="p">)</span> <span class="c1">// 3 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="c1">// 3 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="总结">总结</h2> +<p><code>setup</code>总体来说,用起来真的会更加简洁,而响应式虽然好像比之前麻烦些了,但是一定层面上让开发对对于程序有了更深入的操控。墙裂推荐一波!后面再来详细讲讲对于新特性的体验。</p> + + + + Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + Mon, 25 Oct 2021 16:07:10 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%BA%8C%E5%8A%A8%E6%80%81%E6%A8%A1%E5%9D%97%E7%83%AD%E9%87%8D%E8%BD%BD/ + <h2 id="简介">简介</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a>中,我们了解了如何使用<code>Vite</code>和<code>Electron</code>来快速构建一个Vue3桌面应用。但是,之前构建的应用仅仅是一个简单的版本。在开发过程中,为了更好的开发体验,在开发electron的时候,肯定也希望能有动态模块热重载(HMR),更别说vite那迅雷不及掩耳盗铃儿响叮当之势的加载速度。</p> +<p>因此,接着上一篇文章所完成的项目代码,我们来完成<code>Vite</code>和<code>Electron</code>开发时的动态模块热重载功能。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_2</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,可利用electron中的<code>mainWindow.loadURL(&lt;your-url&gt;)</code>来实现。</p> +<p>对于动态模块热重载功能来说,无论是webpack还是vite,其都是将构建内容存入内存,因此我们无法使用<code>mainWindow.loadFile('dist/index.html')</code>这样加载文件的方式。</p> +<p>但是,单纯地改变该配置也是不行的,需要使用vite将开发服务器运行起来,可以正常运行动态模块热重载,而electron直接加载其开发服务器可访问的url,即<code>http://localhost:3000</code>。</p> +<h2 id="实现步骤">实现步骤</h2> +<h3 id="编辑mainjs">编辑main.js</h3> +<p>将<code>mainWindow.loadFile('dist/index.html')</code>更新为<code>mainWindow.loadURL(&quot;http://localhost:3000&quot;)</code>,更新后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span><span class="s2">&#34;http://localhost:3000&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑viteconfigjs">编辑vite.config.js</h3> +<p>修改文件<code>vite.config.js</code>的<code>base</code>,修改后的文件如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// vite.config.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="s2">&#34;./&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="同时开启vite和electron服务">同时开启vite和electron服务</h3> +<p>为了使vite和electron正常运行,需要先运行vite,使得其开发服务器的url可以正常访问,然后再开启electron去加载url。</p> +<p>此处需要安装两个库:</p> +<ul> +<li><strong>concurrently</strong>:阻塞运行多个命令,<code>-k</code>参数用来清除其它已经存在或者挂掉的进程</li> +<li><strong>wait-on</strong>:等待资源,此处用来等待url可访问</li> +</ul> +<p>首先来安装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D concurrently wait-on +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着更新文件<code>package.json</code>,<code>scripts</code>新增两条命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"> <span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>现已添加两条命令:</p> +<ul> +<li><code>yarn electron</code>为等待tcp协议3000端口可访问,然后执行electron</li> +<li><code>yarn electron:serve</code>为阻塞执行开发服务器运行和<code>yarn electron</code>命令</li> +</ul> +<p>运行项目只要执行命令<code>yarn electron:serve</code>即可,当修改项目文件时,桌面应用也将自动更新。</p> +<h2 id="参考文件">参考文件</h2> +<ul> +<li><a class="link" href="https://cn.vitejs.dev/guide/why.html#slow-server-start" target="_blank" rel="noopener" + >为什么选vite</a></li> +<li><a class="link" href="https://dev.to/brojenuel/vite-vue-3-electron-5h4o" target="_blank" rel="noopener" + >vite+vue3+electron+typescript</a></li> +</ul> + + + + Vite+Electron快速构建一个VUE3桌面应用(三)——打包 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + Tue, 19 Oct 2021 15:26:46 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E4%B8%89%E6%89%93%E5%8C%85/ + <h2 id="简介">简介</h2> +<p>上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a>完成了开发时的动态模块热重载功能,现在是时候来看看怎么完成最后一步——打包了。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3</a></p> +<p>系列文章:</p> +<ul> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/52" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/53" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载</a></li> +<li><a class="link" href="https://github.com/Kuari/Blog/issues/54" target="_blank" rel="noopener" + >Vite+Electron快速构建一个VUE3桌面应用(三)——打包</a></li> +</ul> +<h2 id="思路">思路</h2> +<p>先说结论,重点还是在于<code>mainWindow.loadURL()</code>。</p> +<p>打包后还是加载<code>http://localhost:3000</code>是无法运行的,因此,此处需要先用vite打包好,然后使用<code>electron-builder</code>加载vite打包后的文件进行打包。</p> +<p>为了代码能够根据不同环境在运行时加载<code>http://localhost:3000</code>,在打包时加载文件,此处需要使用环境变量来切换生产和开发环境。</p> +<h2 id="实现">实现</h2> +<h3 id="环境变量">环境变量</h3> +<p>此处使用环境变量<code>NODE_ENV</code>来切换生产和开发环境,生产环境为<code>NODE_ENV=production</code>,开发环境为<code>NODE_ENV=development</code>,若有其它如<code>release</code>等环境可在此基础上拓展。</p> +<h3 id="创建electron文件夹">创建electron文件夹</h3> +<p>在项目根目录下创建文件夹<code>electron</code>,将<code>main.js</code>和<code>preload.js</code>文件移动进来。其结构如下所示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── electron +</span></span><span class="line"><span class="cl">│   ├── main.js +</span></span><span class="line"><span class="cl">│   └── preload.js +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>若还是不太明白可以看看<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_3" target="_blank" rel="noopener" + >源码</a>中文件结构。</p> +<h3 id="编辑electronmainjs">编辑electron/main.js</h3> +<p>该文件主要是需要根据环境变量切换electron加载的内容,修改内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>修改后的完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// electron/main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">NODE_ENV</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.loadFile(&#39;dist/index.html&#39;) 将该行改为下面这一行,加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadURL</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">?</span> <span class="s1">&#39;http://localhost:3000&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="o">:</span><span class="sb">`file://</span><span class="si">${</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;../dist/index.html&#39;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s2">&#34;development&#34;</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">webContents</span><span class="p">.</span><span class="nx">openDevTools</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑packagejson">编辑package.json</h3> +<p>首先修改<code>main </code>属性,将<code>main: main.js</code>改为<code>main: electron/main.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="err">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,编辑<code>build</code>属性:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;build&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.your-website.your-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;ElectronApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 &lt;your-name&gt;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,更新<code>scripts</code>属性。</p> +<p>此处需要先安装两个库:</p> +<ul> +<li><strong><code>cross-env</code></strong>: 该库让开发者只需要注重环境变量的设置,而无需担心平台设置</li> +<li><strong><code>electron-builder</code></strong>: electron打包库</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D cross-env electron-builder +</span></span></code></pre></td></tr></table> +</div> +</div><p>更新后的<code>scripts</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> <span class="c1">// 此处需要设置环境变量以保证开发时加载url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> <span class="c1">// 新增打包命令 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,更新后的<code>package.json</code>完整内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;electron/main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;wait-on tcp:3000 &amp;&amp; cross-env NODE_ENV=development electron .&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;concurrently -k \&#34;yarn dev\&#34; \&#34;yarn electron\&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build &amp;&amp; electron-builder&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;concurrently&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.3.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cross-env&#34;</span><span class="p">:</span> <span class="s2">&#34;^7.0.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron-builder&#34;</span><span class="p">:</span> <span class="s2">&#34;^22.13.1&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;wait-on&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.0.0&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;appId&#34;</span><span class="p">:</span> <span class="s2">&#34;com.my-website.my-app&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;productName&#34;</span><span class="p">:</span> <span class="s2">&#34;MyApp&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Copyright © 2021 kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;public.app-category.utilities&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;nsis&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;oneClick&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;allowToChangeInstallationDirectory&#34;</span><span class="p">:</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;dist/**/*&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;electron/**/*&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;directories&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;buildResources&#34;</span><span class="p">:</span> <span class="s2">&#34;assets&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;output&#34;</span><span class="p">:</span> <span class="s2">&#34;dist_electron&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="打包">打包</h2> +<p>直接执行打包命令即可开始打包。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:build +</span></span></code></pre></td></tr></table> +</div> +</div><p>打包完成之后,会多出两个文件夹<code>dist</code>和<code>dist_electron</code>,其文件结构如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── dist +</span></span><span class="line"><span class="cl">│   ├── assets +</span></span><span class="line"><span class="cl">│   ├── favicon.ico +</span></span><span class="line"><span class="cl">│   └── index.html +</span></span><span class="line"><span class="cl">├── dist_electron +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0-mac.zip.blockmap +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg +</span></span><span class="line"><span class="cl">│   ├── MyApp-0.0.0.dmg.blockmap +</span></span><span class="line"><span class="cl">│   ├── builder-debug.yml +</span></span><span class="line"><span class="cl">│   ├── builder-effective-config.yaml +</span></span><span class="line"><span class="cl">│   └── mac +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>至此,便完成了打包。</p> +<p>后面再来写写关于electron的优化,减少electron打包后应用的体积。(这玩意儿确实打包下来有点大呢/狗头)</p> + + + + Vite+Electron快速构建一个VUE3桌面应用 + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + Mon, 18 Oct 2021 09:58:57 +0000 + + https://blog.hunterji.com/p/vite-electron%E5%BF%AB%E9%80%9F%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAvue3%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8/ + <h2 id="简介">简介</h2> +<p>首先,介绍下<code>vite</code>和<code>Electron</code>。</p> +<ul> +<li>Vite是一种新型前端构建工具,能够显著提升前端开发体验。由尤大推出,其发动态表示“再也回不去webpack了&hellip;”</li> +<li>Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。</li> +</ul> +<p>当开始想用vue去开发一个桌面应用时,首先去搜索下,了解到当前如下两种现成方案:</p> +<ul> +<li><strong>electron-vue</strong>: 该项目集成度较好,封装较为完整,中文搜索下来文章较多也是该方案,可以直接上手去使用。但是,问题在于其内置electron的版本太低,写文章时看到的版本是2.0.4,而最新的electron版本是15.1.2。</li> +<li><strong>Vue CLI Plugin Electron Builder</strong>: 该方案是集成到到<code>vue-cli</code>中使用,使用<code>vue add electron-builder</code>后可直接上手,免去了基础配置的步骤。但是其只能在<code>vue-cli</code>下使用,无法配合<code>vite</code>来使用。</li> +</ul> +<p>因此,若要使用<code>vite</code>和<code>electron</code>,还需要自己来配置。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1" target="_blank" rel="noopener" + >https://github.com/Kuari/Blog/tree/master/Examples/vite_electron/vite_electron_1</a></p> +<h2 id="创建一个vite项目">创建一个Vite项目</h2> +<h3 id="安装-vite">安装 vite</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建项目">创建项目</h3> +<p>创建命令如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite &lt;your-vue-app-name&gt; --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><p>此处创建一个项目,名为kuari。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn create vite kuari --template vue +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="进入且运行">进入且运行</h3> +<p>进入项目,在运行前需要先安装下依赖。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> kuari +</span></span><span class="line"><span class="cl">yarn install +</span></span><span class="line"><span class="cl">yarn dev +</span></span></code></pre></td></tr></table> +</div> +</div><p>在运行命令敲下的一瞬间,几乎是已经在运行了,不愧是vite。此时按照输出,打开地址预览,即可看到初始化页面。</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gveqwmm4eij61bc0u03zy02.jpg" alt="截屏2021-10-14 下午12.50.48" style="zoom:50%;" /> +<p>至此一个基础的vite项目创建完成。</p> +<h2 id="配置electron">配置Electron</h2> +<h3 id="官方文档">官方文档</h3> +<p>在<a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网的快速入门文档</a>中,有官方给出的利用html、javascript、css来创建一个electron应用的案例,vite+electron的方案也借鉴其中。</p> +<h3 id="安装">安装</h3> +<p>首先安装electron至vite应用。目前electron的版本为<code>^15.1.2,</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev electron +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="配置文件">配置文件</h3> +<p>####config.js</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vite&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">vue</span> <span class="nx">from</span> <span class="s1">&#39;@vitejs/plugin-vue&#39;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">path</span> <span class="nx">from</span> <span class="s1">&#39;path&#39;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// https://vitejs.dev/config/ +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">base</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;./dist/&#39;</span><span class="p">),</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vue</span><span class="p">()]</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>main.js</code>,需要注意的是,该内容中<code>index.html</code>的加载路径跟electron官网给的配置不同。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// main.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 控制应用生命周期和创建原生浏览器窗口的模组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;electron&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createWindow</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 创建浏览器窗口 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">800</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">webPreferences</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">preload</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;preload.js&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 加载 index.html +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">loadFile</span><span class="p">(</span><span class="s1">&#39;dist/index.html&#39;</span><span class="p">)</span> <span class="c1">// 此处跟electron官网路径不同,需要注意 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 打开开发工具 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// mainWindow.webContents.openDevTools() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 这段程序将会在 Electron 结束初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 和创建浏览器窗口的时候调用 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 部分 API 在 ready 事件触发后才能使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 打开的窗口,那么程序会重新创建一个窗口。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">createWindow</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;window-all-closed&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="s1">&#39;darwin&#39;</span><span class="p">)</span> <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 在这个文件中,你可以包含应用程序剩余的所有部分的代码, +</span></span></span><span class="line"><span class="cl"><span class="c1">// 也可以拆分成几个文件,然后用 require 导入。 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>####js</p> +<p>创建一个新的文件<code>preload.js</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// preload.js +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="c1">// 所有Node.js API都可以在预加载过程中使用。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 它拥有与Chrome扩展一样的沙盒。 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;DOMContentLoaded&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">replaceText</span> <span class="o">=</span> <span class="p">(</span><span class="nx">selector</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="nx">element</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="nx">text</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">dependency</span> <span class="k">of</span> <span class="p">[</span><span class="s1">&#39;chrome&#39;</span><span class="p">,</span> <span class="s1">&#39;node&#39;</span><span class="p">,</span> <span class="s1">&#39;electron&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">replaceText</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">dependency</span><span class="si">}</span><span class="sb">-version`</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">versions</span><span class="p">[</span><span class="nx">dependency</span><span class="p">])</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####json</p> +<p>为了确保能够运行相关electron的命令,需要修改<code>package.json</code>文件。</p> +<p>首先需要去设置<code>main</code>属性,electron默认会去在开始时寻找项目根目录下的<code>index.js</code>文件,此处我们使用的是<code>main.js</code>,所以需要去定义下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后我们需要新增electron的运行命令。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// package.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;kuari&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;main.js&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vite&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vite build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;vite preview&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron:serve&#34;</span><span class="p">:</span> <span class="s2">&#34;electron .&#34;</span> <span class="c1">// 新增 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^3.2.16&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;@vitejs/plugin-vue&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.9.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;electron&#34;</span><span class="p">:</span> <span class="s2">&#34;^15.1.2&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;vite&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.6.4&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="运行">运行</h2> +<p>直接在终端输入如下命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn electron:serve +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着我们就可以看到我们桌面应用就出来咯!</p> +<img src="https://tva1.sinaimg.cn/large/008i3skNgy1gves3xrayzj612f0u0myy02.jpg" alt="截屏2021-10-14 下午1.32.38" style="zoom:50%;" /> +<h2 id="最后">最后</h2> +<p>之前做项目一直用的Vue CLI Plugin Electron Builder,这次有个项目先用electron开发一下,推一波看看,后期看情况swift重新开发一个mac的桌面应用。也刚好尝尝鲜,一直没有机会试试vite。</p> +<p>electron这个东东确实很方便,就是打包出来的应用体积太大,真的是硬伤啊。这次目标人群首先是windows用户,所以上electron吧!</p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://www.electronjs.org/zh/docs/latest/tutorial/quick-start" target="_blank" rel="noopener" + >Electron官网快速入门</a></li> +<li><a class="link" href="https://vitejs.dev/" target="_blank" rel="noopener" + >Vite官网</a></li> +<li><a class="link" href="https://learnvue.co/2021/05/build-vue-3-desktop-apps-in-just-5-minutes-vite-electron-quick-start-guide/" target="_blank" rel="noopener" + >Build vue3 desktop apps in just 5 minutes</a></li> +</ul> + + + + Vue按需引入ElementUI报错Error -- Plugin -- Preset files are not allowed to export objects, only functions + https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/ + Mon, 10 Aug 2020 00:00:54 +0000 + + https://blog.hunterji.com/p/vue%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5elementui%E6%8A%A5%E9%94%99error--plugin--preset-files-are-not-allowed-to-export-objects-only-functions/ + <h2 id="报错">报错</h2> +<p>按照ElementUI官方文档按需引入却报错,首先报错缺少<code>babel-preset-es2015</code>。安装该组件之后编译却报错。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Error: Plugin/Preset files are not allowed to <span class="nb">export</span> objects, only functions. +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="解决">解决</h2> +<p>该问题为<code>babel</code>版本冲突。</p> +<h3 id="安装插件">安装插件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add @babel/preset-env +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编辑babelrc">编辑<code>.babelrc</code></h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;presets&#34;</span><span class="o">:</span> <span class="p">[[</span><span class="s2">&#34;@babel/preset-env&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="s2">&#34;modules&#34;</span><span class="o">:</span> <span class="kc">false</span> <span class="p">}]],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;plugins&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;component&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;libraryName&#34;</span><span class="o">:</span> <span class="s2">&#34;element-ui&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;styleLibraryName&#34;</span><span class="o">:</span> <span class="s2">&#34;theme-chalk&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue+flask前后端信息传输非对称加密 + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + Fri, 22 May 2020 15:11:05 +0000 + + https://blog.hunterji.com/p/vue-flask%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BF%A1%E6%81%AF%E4%BC%A0%E8%BE%93%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/ + <h2 id="介绍">介绍</h2> +<p>为了前后端传输数据的安全性,需要对数据进行加密。因此选定使用非对称加密,此处为<code>RSA</code>。</p> +<p>在传输数据前,后端生成公钥和私钥,将公钥给前端,前端加密之后,将密文传给后端,后端使用私钥解密即可得到原始的数据。</p> +<h2 id="环境">环境</h2> +<ul> +<li><strong>前端</strong>使用<code>jsencrypt</code>加密</li> +<li><strong>后端</strong>使用<code>Crypto</code>生成密钥和解密</li> +</ul> +<h2 id="后端生成密钥">后端生成密钥</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="前端用公钥加密">前端用公钥加密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="后端使用私钥解密">后端使用私钥解密</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><ul> +<li>这里使用<code>base64</code>先解密一遍是必要的,否则报错<code>ValueError: Ciphertext with incorrect length.</code></li> +</ul> +<h2 id="完整代码">完整代码</h2> +<h3 id="前端">前端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">rsa_en</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">,</span> <span class="nx">target_str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">target_str</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="后端">后端</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_v1_5</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Hash</span> <span class="kn">import</span> <span class="n">SHA</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto</span> <span class="kn">import</span> <span class="n">Random</span> +</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64decode</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_key</span><span class="p">():</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> 生成公钥和私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 返回私钥和公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">rsa</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="n">publick_key</span> <span class="o">=</span> <span class="n">rsa</span><span class="o">.</span><span class="n">publickey</span><span class="p">()</span><span class="o">.</span><span class="n">exportKey</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">private_key</span><span class="o">.</span><span class="n">decode</span><span class="p">(),</span> <span class="n">publick_key</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_encrypt</span><span class="p">(</span><span class="n">public_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa加密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params publick_key: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 需要加密的信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">public_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">public_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">encrypt</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rsa_decrypt</span><span class="p">(</span><span class="n">private_key</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="s2"> rsa解密函数 +</span></span></span><span class="line"><span class="cl"><span class="s2"> +</span></span></span><span class="line"><span class="cl"><span class="s2"> :prams private_key: 私钥 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :params message: 加密后的密文 +</span></span></span><span class="line"><span class="cl"><span class="s2"> :return: 解密后原始信息 +</span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="n">dsize</span> <span class="o">=</span> <span class="n">SHA</span><span class="o">.</span><span class="n">digest_size</span> +</span></span><span class="line"><span class="cl"> <span class="n">sentinel</span> <span class="o">=</span> <span class="n">Random</span><span class="o">.</span><span class="n">new</span><span class="p">()</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span> <span class="o">+</span> <span class="n">dsize</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">private_key</span> <span class="o">=</span> <span class="n">RSA</span><span class="o">.</span><span class="n">import_key</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">cipher_rsa</span> <span class="o">=</span> <span class="n">PKCS1_v1_5</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">private_key</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cipher_rsa</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">b64decode</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">sentinel</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="分段加密">分段加密</h2> +<p><code>RSA</code>加密信息最长为<code>128</code>位,过长则会报错,因此,对于过长的信息需要分段加密,后端也要分段解密后拼装。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">JSEncrypt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;jsencrypt&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">en_str</span><span class="p">(</span><span class="nx">target_str</span><span class="p">,</span> <span class="nx">pubkey</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="cm">/** +</span></span></span><span class="line"><span class="cl"><span class="cm"> 分段加密信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> +</span></span></span><span class="line"><span class="cl"><span class="cm"> :params target_str: 需要加密的信息,此处为很长的信息 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :pubkey: 公钥 +</span></span></span><span class="line"><span class="cl"><span class="cm"> :return: 存储密文的数组 +</span></span></span><span class="line"><span class="cl"><span class="cm"> **/</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">encrypt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">JSEncrypt</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encrypt</span><span class="p">.</span><span class="nx">setPublicKey</span><span class="p">(</span><span class="nx">pubkey</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">en_array</span> <span class="o">=</span> <span class="p">[];</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">n</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">// 每段信息的长度 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">l</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">l</span> <span class="o">/</span> <span class="nx">n</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">message</span> <span class="o">=</span> <span class="nx">target_str</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">n</span> <span class="o">*</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">n</span> <span class="o">*</span> <span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="nx">en_array</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">encrypt</span><span class="p">.</span><span class="nx">encrypt</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">en_array</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue组件props双向绑定 + https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/ + Sat, 21 Mar 2020 23:48:37 +0000 + + https://blog.hunterji.com/p/vue%E7%BB%84%E4%BB%B6props%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A/ + <p>在vue2中不允许子组件直接修改<code>props</code>,为单项数据流,所有若要修改只能通过额外的值,并监听<code>props</code>以改变额外的值。</p> +<h2 id="设置props">设置props</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">props</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="创建额外的值">创建额外的值</h2> +<p>在<code>data</code>中创建一个<code>localDialog</code>,其值为<code>this.dialog</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">localDialog</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="监听">监听</h2> +<p>保持同步的关键在于需要在子组件内监听<code>props</code>,即此处的<code>dialog</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">watch</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="子组件向父组件传递">子组件向父组件传递</h2> +<p>子组件使用<code>this.$emit()</code>即可向父组件传递变化的值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sendToFather</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s1">&#39;dialogchange&#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="父组件调用">父组件调用</h2> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">your-component</span> <span class="na">:dialog</span><span class="o">=</span><span class="s">&#34;dialog&#34;</span> <span class="err">@</span><span class="na">dialogchange</span><span class="o">=</span><span class="s">&#34;dialogchange&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl">data() { +</span></span><span class="line"><span class="cl"> return { +</span></span><span class="line"><span class="cl"> dialog: false +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">}, +</span></span><span class="line"><span class="cl">methods: { +</span></span><span class="line"><span class="cl"> dialogchange(val) { +</span></span><span class="line"><span class="cl"> this.dialog = val +</span></span><span class="line"><span class="cl"> } +</span></span><span class="line"><span class="cl">} +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="完整代码">完整代码</h2> +<h3 id="子组件">子组件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">div</span> <span class="o">:</span><span class="nx">visible</span><span class="o">=</span><span class="s2">&#34;localDialog&#34;</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">hunterji</span><span class="p">.</span><span class="nx">com</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">button</span> <span class="err">@</span><span class="nx">click</span><span class="o">=</span><span class="s2">&#34;sendToFather&#34;</span> <span class="o">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="err">/div&gt;</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/template&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">props</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">localDialog</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">watch</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">sendToFather</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s1">&#39;dialogchange&#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">localDialog</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="父组件">父组件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">component</span> <span class="o">:</span><span class="nx">dialog</span><span class="o">=</span><span class="s2">&#34;dialog&#34;</span> <span class="err">@</span><span class="nx">dialogchange</span><span class="o">=</span><span class="s2">&#34;dialogchange&#34;</span> <span class="o">/&gt;</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/template&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">yourComponent</span> <span class="nx">from</span> <span class="s1">&#39;./yourComponent&#39;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">components</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">yourComponent</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialog</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">dialogchange</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="nx">val</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + vue实现纯前端导入与解析excel表格文件 + https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/ + Thu, 19 Mar 2020 13:11:01 +0000 + + https://blog.hunterji.com/p/vue%E5%AE%9E%E7%8E%B0%E7%BA%AF%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E4%B8%8E%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%96%87%E4%BB%B6/ + <h2 id="场景">场景</h2> +<p>前端导入excel表格,直接前端解析文件,将数据传给后端。</p> +<h2 id="需要的库">需要的库</h2> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install xlsx +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用">使用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">XLSX</span> <span class="nx">from</span> <span class="s2">&#34;xlsx&#34;</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="代码实现">代码实现</h2> +<h3 id="html部分">html部分</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> {{ upload_file || &#34;导入&#34; }} +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">input</span> +</span></span><span class="line"><span class="cl"> <span class="na">type</span><span class="o">=</span><span class="s">&#34;file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">accept</span><span class="o">=</span><span class="s">&#34;.xls,.xlsx&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;upload_file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="err">@</span><span class="na">change</span><span class="o">=</span><span class="s">&#34;readExcel($event)&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js部分">JS部分</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">readExcel</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 读取表格文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">let</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="sr">/\.(xls|xlsx)$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()))</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$message</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;上传格式不正确,请上传xls或者xlsx格式&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;warning&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 更新获取文件名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">upload_file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">ev</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">workbook</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;binary&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">wsname</span> <span class="o">=</span> <span class="nx">workbook</span><span class="p">.</span><span class="nx">SheetNames</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//取第一张表 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">utils</span><span class="p">.</span><span class="nx">sheet_to_json</span><span class="p">(</span><span class="nx">workbook</span><span class="p">.</span><span class="nx">Sheets</span><span class="p">[</span><span class="nx">wsname</span><span class="p">]);</span> <span class="c1">//生成json表格内容 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">ws</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsBinaryString</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="整体代码">整体代码</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span><span class="lnt">69 +</span><span class="lnt">70 +</span><span class="lnt">71 +</span><span class="lnt">72 +</span><span class="lnt">73 +</span><span class="lnt">74 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">{{</span> <span class="nx">upload_file</span> <span class="o">||</span> <span class="s2">&#34;导入&#34;</span> <span class="p">}}</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">input</span> +</span></span><span class="line"><span class="cl"> <span class="na">type</span><span class="o">=</span><span class="s">&#34;file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">accept</span><span class="o">=</span><span class="s">&#34;.xls,.xlsx&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="na">class</span><span class="o">=</span><span class="s">&#34;upload_file&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="nt">@change</span><span class="s">=&#34;readExcel($event)&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">XLSX</span> <span class="nx">from</span> <span class="s2">&#34;xlsx&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">upload_file</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">lists</span><span class="o">:</span> <span class="p">[]</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">submit_form</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 给后端发送请求,更新数据 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;假装给后端发了个请求...&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">readExcel</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 读取表格文件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">let</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="sr">/\.(xls|xlsx)$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()))</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">$message</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;上传格式不正确,请上传xls或者xlsx格式&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;warning&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 更新获取文件名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">upload_file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">ev</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">workbook</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;binary&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">wsname</span> <span class="o">=</span> <span class="nx">workbook</span><span class="p">.</span><span class="nx">SheetNames</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//取第一张表 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">XLSX</span><span class="p">.</span><span class="nx">utils</span><span class="p">.</span><span class="nx">sheet_to_json</span><span class="p">(</span><span class="nx">workbook</span><span class="p">.</span><span class="nx">Sheets</span><span class="p">[</span><span class="nx">wsname</span><span class="p">]);</span> <span class="c1">//生成json表格内容 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">that</span><span class="p">.</span><span class="nx">lists</span> <span class="o">=</span> <span class="p">[];</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 从解析出来的数据中提取相应的数据 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ws</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">item</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">that</span><span class="p">.</span><span class="nx">lists</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">username</span><span class="o">:</span> <span class="nx">item</span><span class="p">[</span><span class="s2">&#34;用户名&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nx">phone_number</span><span class="o">:</span> <span class="nx">item</span><span class="p">[</span><span class="s2">&#34;手机号&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 给后端发请求 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">submit_form</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsBinaryString</span><span class="p">(</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="样式">样式</h2> +<p>原本的文件上传样式可能会跟页面整体风格不搭,所以需要修改其样式。不过此处并不是直接修改其样式而是通过写一个<code>div</code>来覆盖原有的上传按钮。此处样式与<code>element UI</code>中的<code>primary</code>按钮样式相同。</p> +<p>实现该样式的关键在于<code>.upload_file</code>的<code>opacity</code>和<code>position</code>。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">border</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">4</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#409eff</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">40</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">margin-top</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">15</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">min-width</span><span class="p">:</span> <span class="mi">80</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="err">*</span><span class="n">zoom</span><span class="p">:</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">upload_file</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">font-size</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">opacity</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">filter</span><span class="p">:</span> <span class="nf">alpha</span><span class="p">(</span><span class="n">opacity</span><span class="err">=</span><span class="mi">0</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">60</span><span class="kt">px</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="最后">最后</h2> +<p>前端的日益强大导致很多功能都可以在前端去直接实现,并且可以减少服务器压力。</p> +<p>当然单纯地去实现这样的数据传输,尤其对于重要数据,是很不安全的,因此在前后端数据传输的时候,可以加上加密校验,这个后期会来写的。</p> +<h2 id="参考文章">参考文章</h2> +<p>为了实现该功能参考了如下大佬的文章:</p> +<ul> +<li><a class="link" href="https://www.wikimoe.com/?post=157" target="_blank" rel="noopener" + >【Vue 笔记】Vue 读取excel数据并生成数组</a></li> +<li><a class="link" href="https://qxiaomay.github.io/2018/07/13/vue%E5%89%8D%E7%AB%AF%E5%AF%BC%E5%85%A5%E5%B9%B6%E8%A7%A3%E6%9E%90excel%E8%A1%A8%E6%A0%BC%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97/" target="_blank" rel="noopener" + >vue前端导入并解析excel表格操作指南</a></li> +<li><a class="link" href="https://www.haorooms.com/post/css_input_uploadmh" target="_blank" rel="noopener" + >css input[type=file] 样式美化,input上传按钮美化</a></li> +</ul> + + + + vue2实现实时生成二维码和将网页合成图片并在微信内置浏览器长按保存 + https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/ + Sat, 22 Feb 2020 22:43:24 +0000 + + https://blog.hunterji.com/p/vue2%E5%AE%9E%E7%8E%B0%E5%AE%9E%E6%97%B6%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%92%8C%E5%B0%86%E7%BD%91%E9%A1%B5%E5%90%88%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%86%85%E7%BD%AE%E6%B5%8F%E8%A7%88%E5%99%A8%E9%95%BF%E6%8C%89%E4%BF%9D%E5%AD%98/ + <h2 id="url转为二维码">url转为二维码</h2> +<h3 id="需要的库">需要的库</h3> +<p><a class="link" href="https://www.npmjs.com/package/qrcodejs2" target="_blank" rel="noopener" + >qrcodejs2</a></p> +<h3 id="安装">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install qrcodejs2 --save +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入">引入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="实现">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;qrcode&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">GenerateQRcode</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nx">QRCode</span><span class="p">(</span><span class="s2">&#34;qrcode&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="c1">// 此处的qrcode为上面div的id +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">text</span><span class="o">:</span> <span class="nx">目标url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorDark</span><span class="o">:</span> <span class="s2">&#34;#000000&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorLight</span><span class="o">:</span> <span class="s2">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">correctLevel</span><span class="o">:</span> <span class="nx">QRCode</span><span class="p">.</span><span class="nx">CorrectLevel</span><span class="p">.</span><span class="nx">H</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">GenerateQRcode</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="网页保存为图片">网页保存为图片</h2> +<h3 id="需要的库-1">需要的库</h3> +<p><a class="link" href="https://github.com/hongru/canvas2image" target="_blank" rel="noopener" + >html2canvas</a></p> +<h3 id="安装-1">安装</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install html2canvas --save +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入-1">引入</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="实现-1">实现</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">QRCode</span> <span class="nx">from</span> <span class="s2">&#34;qrcodejs2&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputImg</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetDom</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;container&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">html2canvas</span><span class="p">(</span><span class="nx">targetDom</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">canvas</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">outputImg</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="整合">整合</h2> +<p>关于小程序内置浏览器的图片下载,需要一个用来生成图片的块,还需要一个<code>img</code>,先将其隐藏。实现步骤就是首先生成二维码,然后再将html生成图片,最后在html2canvas回调中替换<code>img</code>的<code>src</code>,并将生成图片的块隐藏,将<code>img</code>显示。</p> +<p>当然关于这个实现方式,我看到的技术分享文章中,还有两种不同的解决方式:</p> +<ul> +<li>不需要html来写生成图片的块,而是使用js直接创建;</li> +<li>不需要替换隐藏,将生成的图片覆盖到html生成图片的块之前;</li> +</ul> +<p>这里我只记录一下我使用的,后期会再去研究这两种实现方式。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!--</span><span class="nx">合成的图片</span><span class="err">,</span><span class="nx">默认隐藏</span><span class="err">,</span><span class="nx">合成之后显示</span><span class="o">--&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-show</span><span class="s">=&#34;imgUrl.length&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="nt">:src</span><span class="s">=&#34;imgUrl&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;生成的图片&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;image&#34;</span> <span class="p">/&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c">&lt;!--</span><span class="nx">合成图片需要的html块</span><span class="err">,</span><span class="nx">默认显示</span><span class="err">,</span><span class="nx">合成之后隐藏</span><span class="o">--&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;container&#34;</span> <span class="nt">v-show</span><span class="s">=&#34;!imgUrl.length&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;qrcode&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span><span class="nx">长按识别二维码</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">html2canvas</span> <span class="nx">from</span> <span class="s2">&#34;html2canvas&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">imgUrl</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">outputImg</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetDom</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;container&#34;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">html2canvas</span><span class="p">(</span><span class="nx">targetDom</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">canvas</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 将图片src替换为canvas生成之后转换的url +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">imgUrl</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">GenerateQRcode</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nx">QRCode</span><span class="p">(</span><span class="s2">&#34;qrcode&#34;</span><span class="p">,</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">text</span><span class="o">:</span> <span class="nx">目标url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorDark</span><span class="o">:</span> <span class="s2">&#34;#000000&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">colorLight</span><span class="o">:</span> <span class="s2">&#34;#ffffff&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">correctLevel</span><span class="o">:</span> <span class="nx">QRCode</span><span class="p">.</span><span class="nx">CorrectLevel</span><span class="p">.</span><span class="nx">H</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="nx">resolve</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 先生成二维码 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">GenerateQRcode</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resove</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 再合成图片 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">this</span><span class="p">.</span><span class="nx">outputImg</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 生成之后的图片有点放肆,可以设置宽度来适应手机屏幕 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">.</span><span class="nx">image</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span><span class="o">:</span> <span class="mi">100</span><span class="o">%</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>由此即可实现需要的功能了。</p> +<p>关于后续的优化,需要解决的图片清晰度问题、跨域图片问题等,可以参考<a class="link" href="https://segmentfault.com/a/1190000011478657" target="_blank" rel="noopener" + >这篇文章</a>,这位大佬写得很详细。</p> + + + + + diff --git a/tags/vue/page/1/index.html b/tags/vue/page/1/index.html new file mode 100644 index 0000000..bca0404 --- /dev/null +++ b/tags/vue/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/vue/ + + + + + + diff --git a/tags/vue/page/2/index.html b/tags/vue/page/2/index.html new file mode 100644 index 0000000..2f0a5de --- /dev/null +++ b/tags/vue/page/2/index.html @@ -0,0 +1,563 @@ + + + + +Tag: Vue - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

14 pages

+

Vue

+ +
+
+
+ +
+ + + + + +
+ + + + + +
+
+ + + + + diff --git a/tags/webassembly/index.html b/tags/webassembly/index.html new file mode 100644 index 0000000..3191c46 --- /dev/null +++ b/tags/webassembly/index.html @@ -0,0 +1,626 @@ + + + + +Tag: WebAssembly - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

5 pages

+

WebAssembly

+ +
+
+
+ +
+ + + + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/tags/webassembly/index.xml b/tags/webassembly/index.xml new file mode 100644 index 0000000..38d252e --- /dev/null +++ b/tags/webassembly/index.xml @@ -0,0 +1,2797 @@ + + + + WebAssembly on 开发者小橙 + https://blog.hunterji.com/tags/webassembly/ + Recent content in WebAssembly on 开发者小橙 + Hugo -- gohugo.io + en-us + Thu, 26 Oct 2023 23:51:13 +0000 + 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + Thu, 26 Oct 2023 23:51:13 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E5%9B%9B%E6%9B%B4%E5%B0%8F%E6%9B%B4%E5%B0%8F%E7%9A%84wasm%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(四)——更小更小的wasm文件体积" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(三)——Rust与JS交互》</a>中,讲述了Rust与JS的交互,包括Rust与JS的函数相互调用,比较炸裂的功能就是使用JS调用Rust的struct,JS本身连struct都没有,居然可以调用Rust的struct,这对于Rust开发者的开发体验而言,是真的很棒!</p> +<p>基于前面的系列文章,已经足以使用Rust开发一个完整的功能了。</p> +<p>但是,在前端引入wasm文件时,还是可能存在一些问题,比如wasm文件较大,导致网页访问时间较长,用户体验较差。本篇文章将会通过多种途径来减少Rust编译wasm文件的体积,以减少前端加载wasm文件的时间。</p> +<p>曾经一段时间,我一直用<a class="link" href="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/" target="_blank" rel="noopener" + >Go开发WebAssembly</a>,其编译后的wasm文件体积还是较大的,为了减少wasm文件的体积真是煞费苦心,1xMB大小的wasm文件真的是太痛了&hellip;&hellip;但是体积优化往往都会指向一条“充满魅惑的不归路”——换成Rust开发😆!如果你已经在用Rust开发WebAssembly了,那么恭喜你,对Go而言,在wasm体积上,你已经赢在起跑线上了。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="查看体积">查看体积</h2> +<p>在真正去减少体积前,我们需要来先看一下,当前情况下体积是多少,方便后续对比前后体积。</p> +<p>查看体积的方式有多种,这里推荐几个,(Linux和MacOS)使用其一即可。</p> +<h3 id="ls">ls</h3> +<p>可以使用<code>ls -l</code>或者<code>ll</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ll pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">-rw-r--r-- <span class="m">1</span> kuari staff 23K Jul <span class="m">20</span> 21:52 pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="stat">stat</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ stat pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"><span class="m">16777222</span> <span class="m">142141572</span> -rw-r--r-- <span class="m">1</span> kuari staff <span class="m">0</span> <span class="m">23347</span> <span class="s2">&#34;Jul 20 21:52:53 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="s2">&#34;Jul 20 21:52:01 2023&#34;</span> <span class="m">4096</span> <span class="m">48</span> <span class="m">0</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="wc">wc</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23347</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>以<code>wc</code>为例,当前该wasm文件体积为<strong>23347b</strong>。</p> +<h2 id="代码层面">代码层面</h2> +<blockquote> +<p>Link-Time Optimization (LTO) 是指在程序链接时进行的一种过程间优化(interprocedural optimization)。它允许编译器在链接阶段对多个编译单元进行优化,从而提高程序的性能、可靠性和安全性。</p> +</blockquote> +<p>从代码层面优化,主要是利用LTO(Link-Time Optimization)。</p> +<h3 id="代码内">代码内</h3> +<p>在<code>Cargo.toml</code>中开启LTO:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>开启LTO虽然能够减少编译后的体积,但是也会增加编译时间。</p> +<p>LTO开启后,默认是在减少一定程度的编译体积的情况下,要确保编译的时间。如果你的需求就是更小的体积,而不是较短的时间,那么,可以通过手动指定编译等级来让LTO作出改变。</p> +<p>在代码内可以使用如下等级:</p> +<ul> +<li><code>s</code>:默认的 LTO 等级。它会进行最基本的 LTO 优化,包括内联函数、函数重写、数据重排等</li> +<li><code>z</code>:最高级的 LTO 等级。它会进行更复杂的 LTO 优化,包括死代码消除、内存分配优化、安全性优化等</li> +</ul> +<p>那么可以在<code>Cargo.toml</code>中这么配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="nx">opt-level</span> <span class="p">=</span> <span class="s1">&#39;z&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>原始的文件体积是23347b,现在编译后看一下体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19879</span> pkg/hello_wasm_bg.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>很明显是减少体积!但是,使用<code>z</code>等级并不代表一定每次体积都会比<code>s</code>小的,有时候<code>s</code>也会比<code>z</code>小,这需要视代码情况而定。</p> +<h3 id="代码外">代码外</h3> +<p>在代码外,可以使用<a class="link" href="https://github.com/WebAssembly/binaryen" target="_blank" rel="noopener" + >wasm-opt</a>来进行优化,其可以对 WebAssembly 模块进行多方面的优化,当然本篇文章中重点在体积方面(挖个坑,后面再详聊/狗头)。并且wasm-opt可以对所有符合WebAssembly规范的wasm文件进行优化,所以,就算你不是Rust写的,那也可以用其进行优化。(想想我曾经Go写的wasm,也是有多一个法子可以优化一把了&hellip;&hellip;)</p> +<p>首先,来看一下wasm-opt的基本优化参数:</p> +<ul> +<li><strong>-o</strong>:指定优化后的模块输出文件</li> +<li><strong>-O</strong>:启用默认优化,等同于<code>-Os</code>参数</li> +<li><strong>-O0</strong>:不进行任何优化</li> +<li><strong>-O1</strong>:进行一些基本的优化,例如内联函数优化和死代码消除优化</li> +<li><strong>-O2</strong>:进行更为彻底的优化,例如函数重写、数据重排、内存分配优化等</li> +<li><strong>-O3</strong>:进行最为彻底的优化,包括一些可能影响程序功能的优化</li> +<li><strong>-O4</strong>:与 <code>-O3</code> 相同,但会启用更为激进的优化</li> +<li><strong>-Os</strong>:优化目标是减小代码大小,会进行一些可能影响性能的优化</li> +<li><strong>-Oz</strong>:与 <code>-Os</code> 相同,但会启用更为激进的优化</li> +</ul> +<p>基于本篇文章主题,此处将使用<code>-Os</code>和<code>-Oz</code>两种参数,其于上述&quot;代码内&quot;的等级是对应的。</p> +<p>此处以原始wasm文件,以<code>-Oz</code>参数来执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">23194</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>再以上述开启“代码内“LTO编译后的wasm文件,以wasm-opt执行一下,看一下对比效果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.wasm +</span></span><span class="line"><span class="cl"> <span class="m">19871</span> pkg/output.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>总体而言,wasm文件的体积越来越小。只是当前我这里的案例,是沿用系列文章内容的代码,没有什么实际性复杂代码,再者本身体积已经很小了,所以不会特别有效果。</p> +<h2 id="网络层面">网络层面</h2> +<p>网络层面的话,就是众所周知的在网络传输时,客户端和服务端约定相同的压缩算法,然后服务端给出时进行压缩,客户端接收时进行解压。网络层面可以对传输报文进行压缩,但不丢失信息。</p> +<p>比如大家都很熟悉的gzip压缩算法,不过,压缩算法有好几种:</p> +<ul> +<li>gzip</li> +<li>compress</li> +<li>deflate</li> +<li>br</li> +</ul> +<p>其中gzip也是压缩率最高的了,此处就以gzip为例。</p> +<p>在网络层面,将wasm文件以gzip压缩,减少其在传输时的体积。虽然减少了传输时的体积,但是浏览器在拿到压缩后的数据,需要消耗一定性能来解密。</p> +<h3 id="开启gzip">开启GZIP</h3> +<p>开启GZIP其实简单,只要前后端约定好都用gzip就行了。</p> +<p>首先,前端请求wasm文件时,需要在request header中放入浏览器支持的压缩模式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Accept-Encoding: gzip, deflate +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,服务端收到这个请求后就可以给出服务端也支持的压缩模式,并告诉浏览器服务端将会用什么压缩模式。</p> +<p>跟浏览器通信的方式就是将信息塞到respone header里面:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Content-Encoding: gzip +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样就开启GZIP了。</p> +<p>然后,就是浏览器接收到response的body和header,知道后端使用gzip压缩的,那么浏览器就会自动用gzip来解压,拿到完整的数据了。</p> +<h3 id="服务端支持">服务端支持</h3> +<p>或许你会想问,浏览器能自动解密,那服务端怎么自动加密呢?要后端写代码让文件加密吗?</p> +<p>那当然不是了,直接让http server来完成这个操作。此处以耳熟能详的Nginx为例。</p> +<p>最简单的就是一行配置开启gzip了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span></code></pre></td></tr></table> +</div> +</div><p>也可以指定gzip的一些参数,比如可以加密的类型、最小加密长度等等:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gzip on; +</span></span><span class="line"><span class="cl">gzip_types text/plain application/xml; +</span></span><span class="line"><span class="cl">gzip_proxied no-cache no-store private expired auth; +</span></span><span class="line"><span class="cl">gzip_min_length 1000; +</span></span><span class="line"><span class="cl">... +</span></span></code></pre></td></tr></table> +</div> +</div><p>更多的http server配置,可以去各自官方文档查阅。</p> +<h2 id="物理层面">物理层面</h2> +<p>你可能会惊奇,什么物理层面?!</p> +<p>没错,真就是物理层面——直接对wasm文件进行gzip物理压缩!哈哈,这个方法也真是绝了,我之前在Go开发wasm时,寻找减少体积的时候发现的,如果你的wasm已经优化得穷途末路了,不妨大胆试试这个方案。😆</p> +<p>还记得上面章节“网络层面“中,有个问题就是是否需要手动压缩,那么这里就是全程手动压缩和解压缩了,哈哈。</p> +<h3 id="物理压缩">物理压缩</h3> +<p>首先,是对wasm文件进行物理层面的gzip压缩,此处先使用原始的wasm(23347b):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gzip -c pkg/hello_wasm_bg.wasm &gt; pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,看一下其体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">10403</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果卓群,从23347b减少到了10403b!</p> +<p>然后来把上述“代码层面”的优化来一遍,看一下最后的体积:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ wc -c pkg/output.gz +</span></span><span class="line"><span class="cl"> <span class="m">9237</span> pkg/output.gz +</span></span></code></pre></td></tr></table> +</div> +</div><p>效果更加卓群了,从19871b减少到了9237b了!</p> +<p>所以,此处就是对wasm文件进行物理压缩并存储,然后浏览器请求时,直接请求到<code>.gz</code>文件。</p> +<h3 id="物理解压">物理解压</h3> +<p>浏览器拿到<code>.gz</code>文件后,需要物理解压。</p> +<p>这里推荐使用<code>pako</code>这个前端库,对<code>.gz</code>文件进行解压:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">gunzipWasm</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;target.gz&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">arrayBuffer</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// A fetched response might be decompressed twice on Firefox. +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// See https://bugzilla.mozilla.org/show_bug.cgi?id=610679 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">buffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x1F</span> <span class="o">&amp;&amp;</span> <span class="nx">buffer</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">===</span> <span class="mh">0x8B</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">buffer</span> <span class="o">=</span> <span class="nx">pako</span><span class="p">.</span><span class="nx">ungzip</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)}</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">buffer</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>之后就可以直接使用了。</p> +<h2 id="buff叠加">BUFF叠加</h2> +<p>此处直接将上述所有方法都用起来,直接叠加buff,来看看当前(本系列文章积累的)这个案例能减少多少体积。在“物理层面”章节中,已经累加除了“网络层面”的buff了,所以可以直接使用其结果。而“网络层面”章节中,以gzip来压缩,将gzip的压缩率以40%来估算。</p> +<p>那么最终该案例的wasm体积将在5542b,压缩率大约在77%!</p> +<p>当然,还要算上一个初始的语言buff——Rust,使用Rust本身就已经导致wasm文件体积很小了。</p> +<h2 id="总结">总结</h2> +<p>本片文章中,从代码层面、网络层面、物理层面共三个层面介绍了对wasm文件的体积优化方案,其中共有四个方案。</p> +<p>最后,当前(本系列文章积累的)该案例叠加了所有buff之后,能够减少77%的体积,真的感觉挺棒的了,哈哈。</p> +<p>希望能够对各位有所帮助。</p> + + + + 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + Tue, 27 Jun 2023 00:11:12 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%89rust%E4%B8%8Ejs%E4%BA%A4%E4%BA%92/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(三)——Rust与JS交互" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(二)——DOM和类型转换》</a>中,描述了使用Rust操作DOM,并实现Rust与JS类型转换的多种方法。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>基于上一篇文章中,Rust与JS的类型转换的多种方法,本篇文章继续深入Rust与JS的交互。</p> +<p>首先,Rust与JS的类型转换,可以实现变量的传递,那么变量是要用在哪里呢?那必然是函数了!</p> +<p>所以,本篇文章来讲述一下Rust与JS的函数相互调用,基于此,可以实现大量日常功能开发。</p> +<p>并且,还将会讲述一下,如何导出Rust的struct给JS调用。</p> +<p>是的,没错,在JS调用Rust的struct!一开始看到这个功能的时候,我的脑子是有点炸裂的&hellip;&hellip;😳</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="函数的相互调用">函数的相互调用</h2> +<h3 id="js调用rust函数">JS调用Rust函数</h3> +<p>其实,在本系列文章的第一篇中,就是使用的JS调用Rust函数作为案例来演示的,这里依然以此为例,主要讲一下要点。</p> +<p>首先,声明一个Rust函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处需要注意的要点如下:</p> +<ul> +<li>引入<code>wasm_bindgen</code></li> +<li>声明一个函数,使用<code>pub</code>声明</li> +<li>在函数上使用<code>#[wasm_bindgen]</code>宏来将Rust函数导出为WebAssembly模块的函数</li> +</ul> +<p>接着,编译成wasm文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,在JS中调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server,在浏览器的控制台中可以看到<code>the result from rust is: 3</code>,表明调用成功!</p> +<h3 id="rust调用js函数">Rust调用JS函数</h3> +<p>###1 指定JS对象</p> +<p>在Rust中调用JS函数,需要进行指定JS对象,也就是说,得明确告诉Rust,这个JS函数是从JS哪儿拿来的用的。</p> +<p>主要在于下面两个方式:</p> +<ul> +<li><em><strong>js_namespace</strong></em>: 是一个可选的属性,用于指定一个JavaScript命名空间,其中包含将要在wasm模块中导出的函数。如果没有指定<code>js_namespace</code>,则所有的导出函数将被放置在全局命名空间下。</li> +<li><em><strong>js_name</strong></em>: 是另一个可选属性,它用于指定JavaScript中的函数名称。如果没有指定<code>js_name</code>,则导出函数的名称将与Rust中的函数名称相同。</li> +</ul> +<p>###2 JS原生函数</p> +<p>对于一些JS原生函数,在Rust中,需要去寻找替代方案,比如我们上一篇文章中讲的<code>console.log()</code>函数,是不是觉得好麻烦啊!</p> +<p>那么,你想直接在Rust中调用JS原生函数吗?!</p> +<p>此处,就以<code>console.log()</code>函数为例,直接在Rust中引入并调用,免去替代方案的烦恼。</p> +<p>首先,给出Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如上代码中,<code>call_js_func</code>函数,顾名思义,此处是调用了js函数,并传入参数<code>hello, javascript!</code>。</p> +<p>那么,<code>call_js_func</code>函数上方的代码,我们来一步步解析一下:</p> +<ol> +<li>第一行代码<code>#[wasm_bindgen]</code>是Rust的属性,它告诉编译器将函数导出为WebAssembly模块</li> +<li><code>extern &quot;C&quot;</code>是C语言调用约定,它告诉Rust编译器将函数导出为C语言函数</li> +<li><code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的console对象</li> +<li><code>fn log(message: &amp;str)</code>是一个Rust函数,它接受一个字符串参数,并将其打印到JavaScript中的console对象中</li> +</ol> +<p>此处与JS交互的关键是<code>js_namespace</code>。在Rust中,<code>js_namespace</code>是用于指定JavaScript命名空间的属性。在WebAssembly中,我们可以通过它将函数绑定到JavaScript中的对象上。</p> +<p>在上述代码中,<code>#[wasm_bindgen(js_namespace = console)]</code>告诉编译器将函数绑定到JavaScript中的<code>console</code>对象。这意味着在JS中使用<code>console.log()</code>函数来调用Rust中的<code>log()</code>函数。</p> +<p>因此,类似的原生函数,都可以使用该方法来实现调用。</p> +<p>最后,我们在JS中调用下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">call_js_func</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">call_js_func</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以在浏览器的控制台中看到<code>hello, javascript!</code>。妙啊!</p> +<p>其实对于<code>console.log()</code>而言,还有另一种调用方式,那就是使用<code>js_namespace</code>和<code>js_name</code>同时指定。</p> +<p>或许,你会问,这有什么不同吗?是的,这有些不同。</p> +<p>不知道你是否发现,当前这个案例中,指定了<code>js_namespace</code>为<code>console</code>,但是真实执行的函数是<code>log()</code>,那么这个<code>log</code>函数的指定,其实是体现在Rust中同样的函数名<code>log</code>。也就是说,该案例的<code>log()</code>就是<code>console.log()</code>中的<code>log()</code>。</p> +<p>我们来换个名字看看,将原来的<code>log()</code>换成<code>log2()</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log2</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log2</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译后去控制台看看,就会看到报错:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">TypeError: console.log2 is not a function +</span></span></code></pre></td></tr></table> +</div> +</div><p>因此,当我们使用<code>js_namespace</code>和<code>js_name</code>结合的方式,在此处是可以进行自定义函数名的。</p> +<p>看一下Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(js_namespace = console, js_name = log)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">log_str</span><span class="p">(</span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log_str</span><span class="p">(</span><span class="s">&#34;hello, javascript!&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,重新定义了一个函数<code>log_str</code>,但是其指定了<code>js_namespace = console</code>和<code>js_name = log</code>,那么此处,就可以使用自定义的函数名。</p> +<p>直接编译后,在控制台看一下,可以直接看到正常输出:<code>hello, javascript!</code>。</p> +<p>总结一下,如果没有指定<code>js_name</code>,则 Rust 函数名称将用作 JS 函数名称。</p> +<p>###3 自定义JS函数</p> +<p>在一定场景下,需要使用Rust调用JS函数,比如对于一些对于JS而言更有优势的场景——用JS操作DOM,用Rust计算。</p> +<p>首先,创建一个文件<code>index.js</code>,写入一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">addIt</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当前的文件结构关系如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">. +</span></span><span class="line"><span class="cl">├── Cargo.lock +</span></span><span class="line"><span class="cl">├── Cargo.toml +</span></span><span class="line"><span class="cl">├── README.md +</span></span><span class="line"><span class="cl">├── index.html +</span></span><span class="line"><span class="cl">├── index.js +</span></span><span class="line"><span class="cl">├── pkg +</span></span><span class="line"><span class="cl">│   ├── README.md +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">│   ├── hello_wasm.js +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">│   ├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">│   └── package.json +</span></span><span class="line"><span class="cl">├── src +</span></span><span class="line"><span class="cl">│   └── lib.rs +</span></span><span class="line"><span class="cl">└── target +</span></span><span class="line"><span class="cl"> ├── CACHEDIR.TAG +</span></span><span class="line"><span class="cl"> ├── debug +</span></span><span class="line"><span class="cl"> ├── release +</span></span><span class="line"><span class="cl"> └── wasm32-unknown-unknown +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>index.js</code>和<code>lib.rs</code>,以及<code>hello_wasm_bg.wasm</code>都是不在同一级别的,<code>index.js</code>都在其它两个文件的上一级。记住这个机构关系!</p> +<p>然后,在<code>lib.rs</code>中,指定函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl">##<span class="p">.</span><span class="o">/</span><span class="n">index</span><span class="p">.</span><span class="n">js</span><span class="s">&#34;)] +</span></span></span><span class="line"><span class="cl"><span class="s">extern &#34;</span><span class="n">C</span><span class="s">&#34; { +</span></span></span><span class="line"><span class="cl"><span class="s"> fn addIt(m: i32, n: i32) -&gt; i32; +</span></span></span><span class="line"><span class="cl"><span class="s">} +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>其中,<code>raw_module = &quot;../index.js&quot;</code>的意思是,指定对应的<code>index.js</code>文件,大家应该清楚,此处指定的是刚刚创建的<code>index.js</code>。<code>raw_module</code>的作用就是用来指定js文件的。</p> +<p>这段代码在前端,可以等同于:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">addIt</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;../index.js&#39;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这样在前端都不用引入了,直接在Rust中引入了,感觉还有点奇妙的。</p> +<p>接着,在Rust调用该函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">call_js_func</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addIt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在前端调用,编译后,在浏览器的控制台中可以看到输出结果了!</p> +<p>总结一下,这里有几个注意点:</p> +<ol> +<li>JS的函数必须要export,否则将无法调用;</li> +<li><code>raw_module</code>只能用来指定相对路径,并且,大家可以在浏览器的控制台中注意到,此处的<code>../</code>的相对路径,其实是以wasm文件而言的相对路径,这里一定要注意呀!</li> +</ol> +<h2 id="js调用rust的struct">JS调用Rust的struct</h2> +<p>现在,来点炸裂的,JS调用Rust的struct?!</p> +<p>JS中连struct都没有,这玩意儿导出来会是什么样,得怎么在JS中调用呢?!</p> +<p>首先,定义一个struct,并且声明几个方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[wasm_bindgen(constructor)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_user</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;name is : </span><span class="si">{}</span><span class="s">, age is : </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="p">).</span><span class="n">as_str</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">set_age</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">age</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处,声明了一个struct名为<code>User</code>,包含<code>name</code>和<code>age</code>两个字段,并声明了<code>new</code>、<code>print_user</code>和<code>set_age</code>方法。</p> +<p>其中还有一个未见过的<code>#[wasm_bindgen(constructor)]</code>,<code>constructor</code>用于指示被绑定的函数实际上应该转换为调用 JavaScript 中的 new 运算符。或许你还不太清晰,继续看下去,你就会明白了。</p> +<p>接着,在JS中调用这个struct,和其方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">addIt2</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">User</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;kuari&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">set_age</span><span class="p">(</span><span class="mi">21</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">user</span><span class="p">.</span><span class="nx">print_user</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到,这里的用法就很熟悉了!</p> +<p>大概想一下,在Rust中要如何调用?也就是直接new一个——<code>User::new('kuari', 20)</code>。</p> +<p>此处在JS中,也是如此,先new一个!</p> +<p>然后很自然地调用struct的方法。</p> +<p>编译后,打开浏览器,可以在控制台看到输出:<code>name is : kuari, age is : 21</code>。</p> +<p>其实,或许大家会很好奇,起码我是非常好奇的,Rust的struct在JS中到底是一个怎样的存在呢?</p> +<p>这里直接添加一个<code>console.log(user)</code>,就可以在输出看到。那么到底在JS中是一个怎样的存在呢?请各位动手打印一下看看吧!:P</p> +<h2 id="总结">总结</h2> +<p>本篇文章中,主要讲述了Rust与JS的交互,体现在Rust与JS的相互调用,这是建立在<a class="link" href="https://github.com/Kuari/Blog/issues/73" target="_blank" rel="noopener" + >上一篇文章</a>中类型转换的基础上的。</p> +<p>Rust与JS的函数相互调用的学习成本还是较大的,而且对比Go写wasm,Rust的颗粒度是非常细的,几乎可以说是随心所欲了。</p> +<p>比较炸裂的就是Rust的struct导出给JS用,这对于Rust与JS的交互而言,还是非常棒的体验。</p> + + + + 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + Sun, 18 Jun 2023 18:18:31 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%BA%8Cdom%E5%92%8C%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(二)——DOM和类型转换" /><h2 id="前言">前言</h2> +<p>在上一篇文章<a class="link" href="https://github.com/Kuari/Blog/issues/72" target="_blank" rel="noopener" + >《使用Rust和WebAssembly整花活儿(一)——快速开始》</a>中,描述了如何创建项目和快速生成wasm并在前端中使用,迈出了整花活儿的第一步。</p> +<p>在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。</p> +<p>本篇文章中,将基于上一篇文章中创建的项目来继续开发。</p> +<p>源码:<a class="link" href="https://github.com/Kuari/hello-wasm" target="_blank" rel="noopener" + >github.com/Kuari/hello-wasm</a></p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +<li>web-sys 0.3.64</li> +</ul> +<h2 id="dom">DOM</h2> +<h3 id="配置依赖">配置依赖</h3> +<p>要操作DOM,需要引入新的依赖<code>web-sys</code>,因此,可以配置<code>Cargo.toml</code>中依赖如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>你或许会好奇,这个<code>features</code>是什么,讲真,我一开始很好奇,又没看到什么特别的说明,试错才发现,原来是要手动引入功能依赖&hellip;比如说,当你需要在Rust中使用JS的<code>console</code>,那么你需要在<code>features</code>中加入<code>console</code>。</p> +<h3 id="获取document">获取Document</h3> +<p>在Rust中使用<code>Document</code>,我们需要按照上一步的说明,添加<code>features</code>。那么这里有一个依赖关系,首先在Rust中获取<code>window</code>,然后再获取<code>document</code>。</p> +<p>因此,添加<code>features</code>后如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后在<code>lib.rs</code>中创建一个函数,用来调用<code>document</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>那么,现在就是在Rust中解锁了<code>document</code>,就可以在前端为所欲为了!</p> +<h3 id="操作element">操作Element</h3> +<p>那么开始操作一波,首先得获取到<code>Element</code>&hellip;&hellip;</p> +<p>是的,你没有想错,继续来添加<code>features</code>吧,此处要添加一个<code>Element</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>ok,那么继续。此处设定函数传入两个参数<code>selector</code>和<code>message</code>,然后通过<code>selector</code>获取element,更新值为<code>message</code>参数的值。完整函数如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">update_message</span><span class="p">(</span><span class="n">selector</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">web_sys</span>::<span class="n">window</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load window&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">document</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">document</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load document&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">document</span><span class="p">.</span><span class="n">query_selector</span><span class="p">(</span><span class="n">selector</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to load element&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">element</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">element</span><span class="p">.</span><span class="n">set_inner_html</span><span class="p">(</span><span class="n">message</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">panic!</span><span class="p">(</span><span class="s">&#34;Failed to set inner html&#34;</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编译">编译</h3> +<p>将写完的Rust项目编译成wasm:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在html中调用">在html中调用</h3> +<p>基于上一篇文章的项目中的html,此处添加一个<code>div</code>,id为<code>message</code>,添加调用wasm的<code>update_message</code>函数,代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;message&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">update_message</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> <span class="c1">// 引入update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">update_message</span><span class="p">(</span><span class="s1">&#39;#message&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;h1&gt;Hello, Rust!&lt;/h1&gt;&#39;</span><span class="p">);</span> <span class="c1">// 调用update_message函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="在浏览器验证">在浏览器验证</h3> +<p>启动一个http server,然后在浏览器查看,可以看到在页面上出现一个<code>h1</code>标签的<code>Hello, Rust!</code>。</p> +<h3 id="发现更多方法">发现更多方法</h3> +<p>按照文章来写的过程中,你应该会发现一个问题——怎么这些方法没有补全?!</p> +<p>是的,没错的,(至少我发现)当前<code>web-sys</code>并没有补全,所以只能结合开发者优秀的前端技能和丰富的<a class="link" href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/" target="_blank" rel="noopener" + >官方文档</a>来开发了。</p> +<h2 id="rust与js的类型相互转换">Rust与JS的类型相互转换</h2> +<p>对于wasm而言,性能固然是提升的,但是类型转换一直是个问题。当大量数据需要在wasm/js中进行类型转换时,这对性能来说,真的是个灾难。之前在使用go开发wasm时,就遇到过这样的问题,需要用官方的方法来进行手动类型转换,然而wasm处理的是一个很大的数据量&hellip;&hellip;</p> +<p>不过好在Rust的类型支持真的挺丰富的!</p> +<h3 id="基础类型">基础类型</h3> +<p>基础类型挺简单的,而且Rust的范性也很好地支持了很多类型。如下是基础类型映射表:</p> +<table> +<thead> +<tr> +<th>Rust类型</th> +<th>JavaScript类型</th> +</tr> +</thead> +<tbody> +<tr> +<td>i8</td> +<td>number</td> +</tr> +<tr> +<td>i16</td> +<td>number</td> +</tr> +<tr> +<td>i32</td> +<td>number</td> +</tr> +<tr> +<td>i64</td> +<td>BigInt</td> +</tr> +<tr> +<td>u8</td> +<td>number</td> +</tr> +<tr> +<td>u16</td> +<td>number</td> +</tr> +<tr> +<td>u32</td> +<td>number</td> +</tr> +<tr> +<td>u64</td> +<td>BigInt</td> +</tr> +<tr> +<td>f32</td> +<td>number</td> +</tr> +<tr> +<td>f64</td> +<td>number</td> +</tr> +<tr> +<td>bool</td> +<td>boolean</td> +</tr> +<tr> +<td>char</td> +<td>string</td> +</tr> +<tr> +<td>&amp;str</td> +<td>string</td> +</tr> +<tr> +<td>String</td> +<td>string</td> +</tr> +<tr> +<td>&amp;[T] 例如:&amp;[u8]</td> +<td>[T] 例如:Uint8Array</td> +</tr> +<tr> +<td>Vec<T></td> +<td>Array</td> +</tr> +</tbody> +</table> +<h3 id="基础类型转换示例">基础类型转换示例</h3> +<p>在<code>lib.rs</code>文件中,创建一个函数,挑选几个类型作为参数传入,然后将其读取并打印:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>可以看到该函数传入了JS的<code>number</code>、<code>boolean</code>、<code>Uint8Array</code>和<code>Array</code>四个类型的参数。</p> +<p>然后编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在前端中引入函数并调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_values</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_values</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">,</span> <span class="nx">jsBoolean</span><span class="p">,</span> <span class="nx">jsUint8Array</span><span class="p">,</span> <span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,启动http server并打开浏览器,在控制台可以看到&hellip;看不到?!</p> +<p>是的,没错,Rust的<code>println!</code>只会将打印的内容发送到Rust的标准输出流,而不是前端的控制台。如果想在控制台中打印,那么需要调用JS的<code>console</code>了。</p> +<p>使用新的功能,第一步就是添加<code>features</code>,<code>Cargo.toml</code>中添加<code>console</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在Rust中调用<code>console.log()</code>如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="s">&#34;Hello, Rust!&#34;</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>此处将其封装成一个函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">console_log</span><span class="p">(</span><span class="n">message</span>: <span class="nb">String</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">web_sys</span>::<span class="n">console</span>::<span class="n">log_1</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">.</span><span class="n">into</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,将示例函数的<code>println</code>改成<code>console_log()</code>和<code>format!</code>,函数代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_values</span><span class="p">(</span><span class="n">js_number</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="n">js_uint8_array</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">js_number_array</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_number</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js boolean: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">js_boolean</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_uint8_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js Uint8Array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_number_array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;js number array item: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>最后,编译之后,打开浏览器,就可以在控制台看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">js number: 10 +</span></span><span class="line"><span class="cl">js boolean: true +</span></span><span class="line"><span class="cl">js Uint8Array item: 1 +</span></span><span class="line"><span class="cl">js Uint8Array item: 2 +</span></span><span class="line"><span class="cl">js Uint8Array item: 3 +</span></span><span class="line"><span class="cl">js number array item: 30 +</span></span><span class="line"><span class="cl">js number array item: 40 +</span></span><span class="line"><span class="cl">js number array item: 50 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="通用类型">通用类型</h3> +<p>Rust中提供了一个通用的类型——JsValue,可以作为任何JS类型。</p> +<p>这里给一个简单的案例,设置一个函数,使用<code>JsValue</code>作为参数传入,并打印。</p> +<p>创建函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_js_value</span><span class="p">(</span><span class="n">val</span>: <span class="nc">JsValue</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">val</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后编译成wasm文件。</p> +<p>在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">print_js_value</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumber</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsBoolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsUint8Array</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uint8Array</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">jsUint8Array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">jsNumberArray</span> <span class="o">=</span> <span class="p">[</span><span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">];</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumber</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsBoolean</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsUint8Array</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">print_js_value</span><span class="p">(</span><span class="nx">jsNumberArray</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在html中,传入了不同类型的参数,但是在浏览器的控制台中可以看到,将所有不同类型的参数都打印出来了:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">JsValue(10) +</span></span><span class="line"><span class="cl">JsValue(true) +</span></span><span class="line"><span class="cl">JsValue(Uint8Array) +</span></span><span class="line"><span class="cl">JsValue([30, 40, 50]) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="result">Result</h3> +<p><code>Result</code>在Rust中是一个很重要的存在,经常写Rust的话,也不想在写WebAssembly时改变开发习惯。</p> +<p>其实对于JS而言,<code>Result</code>可以直接在<code>catch</code>中捕获到,只是说,这里我们需要定义好参数类型。</p> +<p>####1 使用Result返回报错</p> +<p>首先来一个只返回报错的场景:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">only_return_error_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>这里返回类型是<code>Result</code>,但是仅仅返回了一个错误。值得注意的是,这里的报错使用的类型是<code>JsError</code>,当然,这里也可以使用<code>JsValue</code>。</p> +<p>然后在html调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">only_return_error_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;1 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">only_return_error_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;100 is ok&#39;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>那么,在浏览器的控制台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">100 is ok +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 使用Result返回正常值和错误</p> +<p>那么,如果想既返回正常值,也想返回错误呢?Rust返回一个<code>Result</code>是没有问题,那么JS怎么解析呢?</p> +<p>直接上Rust代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_all_when_result</span><span class="p">(</span><span class="n">count</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">JsError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">JsError</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;count &lt; 10&#34;</span><span class="p">))</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>该函数,获取到参数后,如果满足条件,加10后返回,否则报错。</p> +<p>那么看看html中如何调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_all_when_result</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 1: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">return_all_when_result</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`get </span><span class="si">${</span><span class="nx">res</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;An error is reported when the input parameter is 100: &#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,正常获取就行了&hellip;&hellip;/捂脸哭</p> +<p>这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了<code>catch</code>来捕获错误。</p> +<p>最后,就是在浏览器的控制台中看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">An error is reported when the input parameter is 1: Error: count &lt; 10 +</span></span><span class="line"><span class="cl">get 110 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="直接引入js类型">直接引入JS类型</h3> +<p>如果你想更直接一点,那么可以直接引入JS类型!这里主要是利用<code>js-sys</code>这个依赖,可以在<a class="link" href="https://docs.rs/js-sys/latest/js_sys/" target="_blank" rel="noopener" + >官方文档</a>上看到很多JS的类型和函数,直接引入即可使用。当然,一定场景下,直接引入的类型,是需要手动转换类型的。</p> +<p>####1 配置依赖</p> +<p>在<code>Cargo.toml</code>中添加<code>js-sys</code>依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span><span class="line"><span class="cl"><span class="nx">web-sys</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.3.64&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Window&#34;</span><span class="p">,</span> <span class="s2">&#34;Document&#34;</span><span class="p">,</span> <span class="s2">&#34;Element&#34;</span><span class="p">,</span> <span class="s2">&#34;console&#34;</span><span class="p">]</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="nx">js-sys</span> <span class="p">=</span> <span class="s2">&#34;0.3.61&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>####2 Uint8Array</p> +<p>首先以<code>Uint8Array</code>举例,在<code>lib.rs</code>头部引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Uint8Array</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后创建一个函数,参数和返回都是<code>Uint8Array</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">print_uint8_array</span><span class="p">(</span><span class="n">js_arr</span>: <span class="nc">Uint8Array</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Uint8Array</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// new Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">new_with_length</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Uint8Array -&gt; vec +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">)</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">to_vec</span><span class="p">().</span><span class="n">iter</span><span class="p">().</span><span class="n">enumerate</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Avoid type conversion +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">js_arr</span><span class="p">.</span><span class="n">length</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">console_log</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{}</span><span class="s"> - the item in js_arr: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">js_arr</span><span class="p">.</span><span class="n">get_index</span><span class="p">(</span><span class="n">index</span><span class="p">)));</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// vec -&gt; Uint8Array +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">arr2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Uint8Array</span>::<span class="n">from</span><span class="p">(</span><span class="n">vec</span><span class="p">.</span><span class="n">as_slice</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">arr2</span><span class="p">.</span><span class="n">clone</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Use the method of the type itself +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">arr</span><span class="p">.</span><span class="n">set_index</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">arr</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>忽略该函数中无意义的逻辑和<code>arr</code>变量的警告,只是为了演示用法。</p> +</blockquote> +<p>可以在代码中看到,直接引入的<code>Uint8Array</code>有自己的方法,一定场景下,需要转换类型,但是最好避免进行类型转换,而直接使用其自带的方法。</p> +<p>这里可以简要总结下,就是最好一定场景内全部使用直接引入的JS类型,或者直接全部使用Rust类型来代替JS类型,两者都存在场景下,手动转换类型是件很糟糕的事。</p> +<p>####3 Date</p> +<p><code>Date</code>类型,在上面的篇章中都没有提及,这里可以直接引入JS的<code>Date</code>类型来使用。</p> +<p>首先是引入类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">js_sys</span>::<span class="n">Date</span><span class="p">;</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>然后,创建一个函数,返回时间戳:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">return_time</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">f64</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Date</span>::<span class="n">new_0</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">date</span><span class="p">.</span><span class="n">get_time</span><span class="p">()</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>接着,在html中调用:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">return_time</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;current time: &#39;</span><span class="p">,</span> <span class="nx">return_time</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>最后,在浏览器的控制台中,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">current time: 1686979932833 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="总结">总结</h2> +<p>本文中,主要讲述了如何使用Rust来实现DOM操作,读者可以根据方法自己去找到合适的方法,来实现自己的场景。其次,还讲述了Rust与JS的类型转换,从基础的各自类型的映射,到Rust独有的<code>Result</code>,到直接引入JS类型。当然这里需要注意的是,直接引入JS类型和Rust的基础类型映射JS类型这两种方法尽量不要混用,混用会导致需要手动类型转换,造成性能损耗。</p> +<p>至此,又向Rust和WebAssembly整花活儿迈进了一步~😼</p> + + + + 使用Rust和WebAssembly整花活儿(一)——快速开始 + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + Wed, 14 Jun 2023 17:32:28 +0000 + + https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/ + <img src="https://blog.hunterji.com/p/%E4%BD%BF%E7%94%A8rust%E5%92%8Cwebassembly%E6%95%B4%E8%8A%B1%E6%B4%BB%E5%84%BF%E4%B8%80%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/cover.jpg" alt="Featured image of post 使用Rust和WebAssembly整花活儿(一)——快速开始" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<p>之前写过一篇文章,是关于如何使用golang来开发WebAssembly的——<a class="link" href="https://blog.hunterji.com/p/webassembly--%e6%9c%aa%e6%9d%a5%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e7%9a%84%e5%bf%85%e5%a4%87%e6%8a%80%e8%83%bd" target="_blank" rel="noopener" + >WebAssembly:未来前端开发的必备技能</a>。</p> +<p>Rust和Go都可以用来开发WebAssembly,但它们有各自的优势和劣势。</p> +<p>Rust的优点:</p> +<ul> +<li>更快的性能和更小的二进制文件</li> +<li>更好的内存安全性</li> +</ul> +<p>Go的优点:</p> +<ul> +<li>更容易上手和学习</li> +<li>更好的生态系统和社区支持</li> +</ul> +<p>综合来说,如果你更注重性能和内存安全性,那么Rust可能是更好的选择。而如果你更注重开发效率和易用性,那么Go可能更适合你。当然,实际情况还需要根据具体的项目需求和团队情况来选择。</p> +<p>因为一些工作需求,最近整了些rust的花活儿,这里系统地记录一下。当你遇到更注重性能和内存安全性的场景,希望这能有帮助。</p> +<h2 id="环境">环境</h2> +<ul> +<li>Rust 1.70.0</li> +<li>wasm-bindgen 0.2.87</li> +</ul> +<h2 id="创建项目并添加依赖">创建项目并添加依赖</h2> +<p>此处默认已经安装Rust,需要安装的小伙伴儿可以参考<a class="link" href="https://www.rust-lang.org/tools/install" target="_blank" rel="noopener" + >官网</a>。</p> +<p>使用Cargo创建一个名为<code>hello-wasm</code>的项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo new --lib hello-wasm +</span></span></code></pre></td></tr></table> +</div> +</div><p>进入项目,打开文件<code>Cargo.toml</code>,添加依赖:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"><span class="nx">wasm-bindgen</span> <span class="p">=</span> <span class="s2">&#34;0.2.87&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="更新librs">更新lib.rs</h2> +<p>默认创建项目中,存在一个名为<code>lib.rs</code>的文件,将内容全部替换成:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">wasm_bindgen</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[wasm_bindgen]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="编译">编译</h2> +<p>至此,我们创建了一个最简单的功能——一个返回两个整数相加结果的函数。</p> +<p>然后我们可以进行编译了,编译之前需要安装一个工具<code>wasm-pack</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo install wasm-pack +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wasm-pack build --target web +</span></span></code></pre></td></tr></table> +</div> +</div><p>编译完成之后,将会多出来一个<code>pkg</code>文件夹,内容如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pkg +</span></span><span class="line"><span class="cl">├── hello_wasm.d.ts +</span></span><span class="line"><span class="cl">├── hello_wasm.js +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm +</span></span><span class="line"><span class="cl">├── hello_wasm_bg.wasm.d.ts +</span></span><span class="line"><span class="cl">└── package.json +</span></span></code></pre></td></tr></table> +</div> +</div><p>虽然文件很多,但是首先我们看到了我们所需要的wasm文件,并且,根据go的wasm引入方式,这里我们或许会需要用到js文件。</p> +<h2 id="前端引入">前端引入</h2> +<p>为了方便最快校验,直接在<code>hello-wasm</code>项目中创建<code>index.html</code>文件,来进行前端引入。</p> +<h3 id="创建indexhtml">创建index.html</h3> +<p>那么,首先,创建<code>index.html</code>文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错!这是一场标准的开局!😼</p> +<h3 id="引入wasm">引入WASM</h3> +<p>其实不同于go语言的wasm引入方式,Rust更希望直接引入js文件,而不是让开发者手动引入wasm文件。</p> +<p>这里使用js引入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="完整代码">完整代码</h3> +<p>完整的html代码如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>使用Rust和WebAssembly整花活儿<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> Hello, World! +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">import</span> <span class="nx">init</span><span class="p">,</span> <span class="p">{</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./pkg/hello_wasm.js&#39;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nx">init</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`the result from rust is: </span><span class="si">${</span><span class="nx">result</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">run</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="验证">验证</h2> +<p>这里可以快速起一个http server,这里我选择使用<code>http-server</code>,也可以使用<code>python3 -m http.server</code>这样的方式,看怎么各自的使用习惯。</p> +<p>那么,启动http server:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">http-server +</span></span></code></pre></td></tr></table> +</div> +</div><p>打开浏览器,访问<code>http://localhost:8080</code>,打开调试器,即可看到输出<code>the result from rust is: 3</code>,这就意味着迈出了整花活儿的第一步!</p> +<p><img src="https://user-images.githubusercontent.com/25321169/245747861-4fb71bd2-9f90-41b3-ada8-6e1d930044d4.png" + + + + loading="lazy" + + alt="rust_wasm" + + +></p> +<h2 id="常见问题">常见问题</h2> +<h3 id="前端报响应类型错误">前端报响应类型错误</h3> +<p>详细报错如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Failed to load module script: The server responded with a non-JavaScript MIME type of &#34;application/wasm&#34;. Strict MIME type checking is enforced for module scripts per HTML spec. +</span></span></code></pre></td></tr></table> +</div> +</div><p>当引入WebAssembly生成的js文件时,可能会遇到这个报错。报错乍一看是http server的响应问题,或者搜索时候,也会有帖子说这是一个response问题。</p> +<p>实际上,当按照这个文章一步步操作时是不会有这个问题的,是因为本文的编译参数是直接解决了这个问题的。当我自己摸索的时候,解决这个问题真的是看到人都麻了……</p> +<p>关键在于编译命令的参数:<code>--target</code>。</p> +<p>当没有设置这个参数时,默认的参数其实是<code>--target bundler</code>,其是编译成给webpack之类的脚手架使用的。因此这里使用<code>—target web</code>,则是使其编译成可直接在web中使用。</p> +<p>相关参数如下:</p> +<ul> +<li><strong>bundler</strong>:编译成给webpack之类的脚手架使用</li> +<li><strong>web</strong>:编译成web可直接使用</li> +<li><strong>nodejs</strong>:编译成可通过require来加载的node模块</li> +<li><strong>deno</strong>:编译成可通过import加载的deno模块</li> +<li><strong>no-modules</strong>:跟web类似,但是更旧,且不能使用es模块</li> +</ul> +<h3 id="直接引入wasm文件">直接引入wasm文件</h3> +<p>若此时尝试直接引入wasm文件,而不是使用本文所述的方式,那么你会发现,也是可行的!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;./pkg/hello_wasm_bg.wasm&#34;</span><span class="p">),</span> <span class="p">{}).</span><span class="nx">then</span><span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;the result from rust is: &#39;</span><span class="p">,</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>是的,没错,当前是可行的,但是当引入了一些别的比如dom之类的,就坏起来了……</p> +<h3 id="更新的wasm引入方式">更新的wasm引入方式</h3> +<p>上一问题中,且不说是否可以直接引入wasm文件,这里仅说一下,<code>instantiateStreaming</code>这个方法。这是一个更新的方法,无需转成<code>arrayBuffer</code>,这也是摸索Rust整活儿时候发现的。如果在别的语言引入wasm,请使用这个<a class="link" href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming" target="_blank" rel="noopener" + >更新的方法</a>吧。</p> + + + + WebAssembly -- 未来前端开发的必备技能 + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + Thu, 09 Feb 2023 13:15:34 +0000 + + https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/ + <img src="https://blog.hunterji.com/p/webassembly--%E6%9C%AA%E6%9D%A5%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/cover.jpg" alt="Featured image of post WebAssembly -- 未来前端开发的必备技能" /><h2 id="前言">前言</h2> +<p><a class="link" href="https://developer.mozilla.org/zh-CN/docs/WebAssembly" target="_blank" rel="noopener" + >WebAssembly</a> 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。</p> +<h2 id="快速上手">快速上手</h2> +<h3 id="用go写一个hello-world">用go写一个hello world</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello, WebAssembly!&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="将go文件编译成wasm文件">将go文件编译成wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">GOOS</span><span class="o">=</span>js <span class="nv">GOARCH</span><span class="o">=</span>wasm go build -o static/main.wasm +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="拷贝出wasm_execjs">拷贝出wasm_exec.js</h3> +<p>该文件为go的wasm的js支持文件</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cp <span class="s2">&#34;</span><span class="k">$(</span>go env GOROOT<span class="k">)</span><span class="s2">/misc/wasm/wasm_exec.js&#34;</span> static +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="html文件调用wasm文件">html文件调用wasm文件</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="验证调用">验证调用</h3> +<p>浏览器加载html文件,f12打开控制台,可以看到wasm的打印消息。</p> +<h2 id="go与js的类型转换">go与js的类型转换</h2> +<h3 id="类型映射">类型映射</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><p>如上为官方给出的go与js的类型映射表。</p> +<p>比如在go中调用js函数,参数为<code>array</code>,那么就可以直接将go的<code>[]interface{}</code>类型的变量作为参数使用。</p> +<h3 id="函数转换数组">函数转换数组</h3> +<p><code>syscall/js</code>提供了两个函数:</p> +<ul> +<li>CopyBytesToGo:<code>func CopyBytesToGo(dst []byte, src Value) int</code></li> +<li>CopyBytesToJS:<code>func CopyBytesToJS(dst Value, src []byte) int</code></li> +</ul> +<p>两者对于go而言,类型都是<code>[]byte</code>,但是对于js而言,需要<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型,否则就会报错。</p> +<p>那么,如何在go中生成一个<code>Uint8Array</code>或者<code>Uint8ClampedArray</code>类型的变量呢?官方的类型映射表也没有啊&hellip;那么就看下一步。</p> +<h3 id="其余类型">其余类型</h3> +<p>对于非官方类型映射表内的类型,和官方提供的两个数据类型转换之外的类型,可以通过一种通用的方式来生成,以上一步的<code>Uint8Array</code>为例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">length</span><span class="o">&gt;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>实际使用案例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// goData []byte{...} +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">jsData</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Uint8Array&#34;</span><span class="p">).</span><span class="nx">New</span><span class="p">(</span><span class="nx">len</span><span class="p">(</span><span class="nx">goData</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nx">CopyBytesToJS</span><span class="p">(</span><span class="nx">jsData</span><span class="p">,</span> <span class="nx">goData</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>那么,比如js中的<code>Date</code>类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">dateConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Global</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s2">&#34;Date&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">dateConstructor</span><span class="p">.</span><span class="nx">New</span><span class="p">(</span><span class="s2">&#34;2020-10-01&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="极端情况">极端情况</h3> +<p>好吧,还有最后一个方案,如果遇到极端情况,上述方案都无法解决,那么请转换成字符串吧!让go和js用各自的方法分别处理一波,得到自己想要的结果或者给出各自想给的数据。</p> +<h2 id="js调用go函数">js调用go函数</h2> +<blockquote> +<p>此处需要在go中引入<a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a>,以实现js相关的操作。</p> +</blockquote> +<h3 id="注册go函数">注册go函数</h3> +<p>将go的函数注册为js的函数,由js来进行调用。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleCount</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">count</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Int</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">js</span><span class="p">.</span><span class="nf">ValueOf</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleEvent&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleEvent</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>js.Func()</code> 接受一个函数类型作为其参数,该函数的定义是固定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"><span class="c1">// this 即 JavaScript 中的 this +</span></span></span><span class="line"><span class="cl"><span class="c1">// args 是在 JavaScript 中调用该函数的参数列表。 +</span></span></span><span class="line"><span class="cl"><span class="c1">// 返回值需用 js.ValueOf 映射成 JavaScript 的值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>js.ValueOf返回作为js的值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">| Go | JavaScript | +</span></span><span class="line"><span class="cl">| ---------------------- | ---------------------- | +</span></span><span class="line"><span class="cl">| js.Value | [its value] | +</span></span><span class="line"><span class="cl">| js.Func | function | +</span></span><span class="line"><span class="cl">| nil | null | +</span></span><span class="line"><span class="cl">| bool | boolean | +</span></span><span class="line"><span class="cl">| integers and floats | number | +</span></span><span class="line"><span class="cl">| string | string | +</span></span><span class="line"><span class="cl">| []interface{} | new array | +</span></span><span class="line"><span class="cl">| map[string]interface{} | new object | +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用">js调用</h3> +<p>在js中使用也非常简单,引入wasm文件之后,直接调用函数即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleEvent</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">// 传入参数1 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go调用js函数">go调用js函数</h2> +<p>如果在js中本身已经定义了函数,那么在go中也可以直接调用该函数,进行运算,将得出的结果在go中继续使用。</p> +<h3 id="定义js函数">定义js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="go中调用js函数">go中调用js函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;add&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="c1">// 此处输出类型为js.Value,无法直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nf">Int</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// 使用.Int()将其转换为go中的类型,即可直接使用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="引入wasm">引入wasm</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">m</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span> <span class="o">+</span> <span class="nx">n</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="结果">结果</h3> +<p>在前端调试台可以看到输出:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;number: 3&gt; +</span></span><span class="line"><span class="cl">4 +</span></span></code></pre></td></tr></table> +</div> +</div><p>第一个结果就是js.Value的值,第二个结果则是转换成go的值,并按照逻辑进行了<code>+1</code>处理。</p> +<h2 id="回调函数解决go函数阻塞问题">回调函数/解决go函数阻塞问题</h2> +<blockquote> +<p>The Go function fn is called with the value of JavaScript&rsquo;s &ldquo;this&rdquo; keyword and the arguments of the invocation. The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.</p> +<p>Invoking the wrapped Go function from JavaScript will pause the event loop and spawn a new goroutine. Other wrapped functions which are triggered during a call from Go to JavaScript get executed on the same goroutine.</p> +<p>As a consequence, if one wrapped function blocks, JavaScript&rsquo;s event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.</p> +</blockquote> +<p><code>syscall/js</code>官方文档表明,如果go包装函数阻塞,那么js的事件循环也将被阻塞,直到函数返回,调用任何需要事件循环(如fetch)的异步js api都导致立即死锁。因此,一个阻塞函数应该显式地启动一个新的协程。</p> +<p>此处,可以在go中注册一个回调函数,加上协程实现异步,不会产生堵塞。</p> +<h3 id="注册函数">注册函数</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">username</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">String</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">callback</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;hello, %s !&#34;</span><span class="p">,</span> <span class="nx">username</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-1">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">HandleRender</span><span class="p">(</span><span class="s2">&#34;tom&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">hello, tom ! // 隔了3秒之后,输出了回调函数的值 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="go实现promise">Go实现Promise</h2> +<p>上一步的回调函数,解决了函数阻塞问题,此处,结合回调函数实现promise,来丰富异步场景。</p> +<p>在js中,promise是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>async</code>和<code>await</code>调用,拿到结果:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">printMessage</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="mi">3000</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">message</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>在go中又如何构建promise呢?这里可以用到上述go与js的类型转换,创建一个promise:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="注册函数-1">注册函数</h3> +<p>go的完整实现如下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">handler</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span> <span class="o">:=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">resolve</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="s">&#34;hello, world !&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}()</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;waiting...&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">promiseConstructor</span> <span class="o">:=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Promise&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">promiseConstructor</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="js调用-2">js调用</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;static/wasm_exec.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Go</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">instantiateStreaming</span><span class="p">(</span><span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;static/main.wasm&#34;</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">go</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;#button&#39;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">HandleRender</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="输出-1">输出</h3> +<p>在浏览器调试台,可以看到:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting... // 先输出了waiting... +</span></span><span class="line"><span class="cl">message: hello, world ! // 隔了3秒之后输出 +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="操作dom">操作DOM</h2> +<h3 id="使用document">使用document</h3> +<p>定义一个全局的document</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">var docuemnt = js.Global().Get(&#34;document&#34;) +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="获取元素">获取元素</h3> +<p>获取一个<code>id</code>为<code>container</code>的<code>div</code>,设置<code>background-color: red</code>、<code>widht: 600</code>、<code>height: 400</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;container&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">containerElementStyle</span> <span class="p">=</span> <span class="nx">container</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;style&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;background&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="s">&#34;600px&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">containerElementStyle</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="s">&#34;400px&#34;</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建元素">创建元素</h3> +<p>创建一个<code>id</code>为<code>image</code>的<code>image</code>,设置<code>width:300</code>、<code>height:200</code></p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">imageElement</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;createElement&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加子元素">添加子元素</h3> +<p>将<code>image</code>添加为<code>id</code>为<code>container</code>的<code>div</code>的子元素</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">containerElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;appendChild&#34;</span><span class="p">,</span> <span class="nx">imageElement</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加事件">添加事件</h3> +<p>给<code>image</code>添加右击事件,右击<code>image</code>则阻止右键菜单</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 定义响应函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">handlePreventEventCallBack</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;preventDefault&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 给image添加事件 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">imageElement</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;contextmenu&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handlePreventEventCallBack</span><span class="p">))</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>这里需要注意的是,当不再调用响应事件函数时,必须调用Func.Release以释放资源:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">cb</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Func</span> +</span></span><span class="line"><span class="cl"><span class="nx">cb</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="nx">any</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;button clicked&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">cb</span><span class="p">.</span><span class="nf">Release</span><span class="p">()</span> <span class="c1">// 如果不再单击该按钮,则释放该函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;myButton&#34;</span><span class="p">).</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;addEventListener&#34;</span><span class="p">,</span> <span class="s">&#34;click&#34;</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="canvas">Canvas</h2> +<p>这里放一波canvas的案例,包含了一些常用方法,可以参考完成更多操作。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;math&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="s">&#34;syscall/js&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="nx">width</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">height</span> <span class="p">=</span> <span class="mi">400</span> +</span></span><span class="line"><span class="cl"> <span class="nx">radius</span> <span class="p">=</span> <span class="mi">200</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">document</span> <span class="p">=</span> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;document&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleRender</span><span class="p">(</span><span class="nx">this</span> <span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="nx">js</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">canvas</span> <span class="p">=</span> <span class="nx">document</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getElementById&#34;</span><span class="p">,</span> <span class="s">&#34;canvas&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;width&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;height&#34;</span><span class="p">,</span> <span class="nx">height</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;getContext&#34;</span><span class="p">,</span> <span class="s">&#34;2d&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;beginPath&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;arc&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="nx">math</span><span class="p">.</span><span class="nx">Pi</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;lightpink&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fill&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;lineWidth&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;strokeStyle&#34;</span><span class="p">,</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;stroke&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;font&#34;</span><span class="p">,</span> <span class="s">&#34;20px Comic Sans MS&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;fillStyle&#34;</span><span class="p">,</span> <span class="s">&#34;blue&#34;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Call</span><span class="p">(</span><span class="s">&#34;fillText&#34;</span><span class="p">,</span> <span class="s">&#34;Hello, World !&#34;</span><span class="p">,</span> <span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="o">-</span><span class="mi">60</span><span class="p">,</span> <span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">js</span><span class="p">.</span><span class="nf">Global</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HandleRender&#34;</span><span class="p">,</span> <span class="nx">js</span><span class="p">.</span><span class="nf">FuncOf</span><span class="p">(</span><span class="nx">handleRender</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>渲染结果:</p> +<p><img src="https://user-images.githubusercontent.com/25321169/217725808-5dd94da2-bb43-4129-95a9-27f3f85daba5.png" + + + + loading="lazy" + + alt="go_wasm_canvas" + + +></p> +<h2 id="参考文档">参考文档</h2> +<ul> +<li><a class="link" href="https://pkg.go.dev/syscall/js" target="_blank" rel="noopener" + >syscall/js</a></li> +<li><a class="link" href="https://github.com/golang/go/wiki/WebAssembly" target="_blank" rel="noopener" + >go wiki WebAssembly</a></li> +<li><a class="link" href="https://geektutu.com/post/quick-go-wasm.html" target="_blank" rel="noopener" + >Go WebAssembly (Wasm) 简明教程</a></li> +<li><a class="link" href="https://withblue.ink/2020/10/03/go-webassembly-http-requests-and-promises.html" target="_blank" rel="noopener" + >Go, WebAssembly, HTTP requests and Promises</a></li> +</ul> + + + + + diff --git a/tags/webassembly/page/1/index.html b/tags/webassembly/page/1/index.html new file mode 100644 index 0000000..de569ba --- /dev/null +++ b/tags/webassembly/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/webassembly/ + + + + + + diff --git "a/tags/\345\211\215\347\253\257/index.html" "b/tags/\345\211\215\347\253\257/index.html" new file mode 100644 index 0000000..b2b0ae0 --- /dev/null +++ "b/tags/\345\211\215\347\253\257/index.html" @@ -0,0 +1,537 @@ + + + + +Tag: 前端 - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

前端

+ +
+
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git "a/tags/\345\211\215\347\253\257/index.xml" "b/tags/\345\211\215\347\253\257/index.xml" new file mode 100644 index 0000000..3d53516 --- /dev/null +++ "b/tags/\345\211\215\347\253\257/index.xml" @@ -0,0 +1,475 @@ + + + + 前端 on 开发者小橙 + https://blog.hunterji.com/tags/%E5%89%8D%E7%AB%AF/ + Recent content in 前端 on 开发者小橙 + Hugo -- gohugo.io + en-us + Thu, 25 Nov 2021 17:07:55 +0000 + uniapp canvas生成海报功能拆解和问题记录 + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + Thu, 25 Nov 2021 17:07:55 +0000 + + https://blog.hunterji.com/p/uniapp-canvas%E7%94%9F%E6%88%90%E6%B5%B7%E6%8A%A5%E5%8A%9F%E8%83%BD%E6%8B%86%E8%A7%A3%E5%92%8C%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/ + <h2 id="前言">前言</h2> +<p>最近在用uniapp开发小程序,需要用到canvas画海报然后再保存本地。</p> +<p>之前写过同样功能的<a class="link" href="https://github.com/Kuari/Blog/issues/1" target="_blank" rel="noopener" + >文章</a>,不过场景不同,之前是在web上生成海报,该场景可以使用之前文章的方法——html转canvas来实现。</p> +<p>但是uniapp则不同,该框架是去DOM化的,因此只能使用uniapp的官方canvas来实现。</p> +<h2 id="功能拆解">功能拆解</h2> +<p>网上找到的文章,有几篇写得挺好的,展现了完整的功能。这里我把用到的几个功能拆解出来,而不用先通读整篇代码。</p> +<h3 id="创建canvas">创建canvas</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">canvas</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width: 300px; height: 200px;&#34;</span> <span class="na">canvas-id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;firstCanvas&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">canvas</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">view</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">onReady</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 初始化 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="背景色">背景色</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;red&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 此处其实绘制了一个300x200的红色矩形,但是其大小跟canvas大小相同即为背景色了 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="加载图片">加载图片</h3> +<h4 id="1本地图片">1)本地图片</h4> +<p>图片直接在项目中,可以直接加载。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="s2">&#34;./background.png&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="2url">2)url</h4> +<p>此处url返回的为文件流,在uniapp中无法直接加载,需要转换成本地信息才可以使用。</p> +<p>有两种方式可以使用,小程序都需要添加download合法域名:</p> +<ul> +<li><em><strong>uni.getImageInfo</strong></em>:获取文件信息,我使用的这个方法</li> +<li><em><strong>uni.downloadFile</strong></em>:下载文件</li> +</ul> +<p>原生使用方法是这样的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>由于js的异步问题,如果图片较大或者多个图片的情况下,会有这边还没加载完,canvas就已经绘制完了的情况,所以这里将其优化下。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 首先封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">url</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">getImageInfo</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">src</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">path</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">url</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h4 id="3base64">3)base64</h4> +<p>如果你的图片数据是base64的,那恭喜你,依然加载不了。当然这存在的情况是,微信开发工具是没有问题的,但是上了真机之后直接无法加载了,这波是小程序的锅。</p> +<p>这里呢需要将图片存储然后用本地地址绘制。</p> +<p>原生方法:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">})</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>优化一波:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 封装 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">getBase64ImageInfo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">base64Data</span><span class="o">:</span> <span class="nx">string</span><span class="p">)</span><span class="o">:</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">string</span><span class="o">&gt;</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">getFileSystemManager</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">times</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">codeImg</span> <span class="o">=</span> <span class="nx">wx</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">USER_DATA_PATH</span> <span class="o">+</span> <span class="s1">&#39;/&#39;</span> <span class="o">+</span> <span class="nx">times</span> <span class="o">+</span> <span class="s1">&#39;.png&#39;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">rej</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">imgPath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="o">:</span> <span class="nx">base64Data</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">encoding</span><span class="o">:</span> <span class="s1">&#39;base64&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">genPoster</span> <span class="o">=</span> <span class="kr">async</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">getBase64ImageInfo</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">base64</span><span class="o">-</span><span class="nx">data</span><span class="o">&gt;</span><span class="p">)</span> <span class="c1">// 建议所有图片在开始绘制canvas前加载好 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">uni</span><span class="p">.</span><span class="nx">createCanvasContext</span><span class="p">(</span><span class="s1">&#39;firstCanvas&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">imgPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">draw</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="文字">文字</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFontSize</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">font</span> <span class="o">=</span> <span class="s2">&#34;nomarl bold 13px Arial,sans-serif&#34;</span> <span class="c1">// 加粗等功能 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">setFillStyle</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">fillText</span><span class="p">(</span><span class="s2">&#34;hello, world !&#34;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="圆角矩形">圆角矩形</h3> +<p>想要绘制一个圆角的矩形,啊&hellip;&hellip;这波就复杂了,原理就不细讲了,直接上代码,调用即可。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">roundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">width</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">height</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">arcTo</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">radius</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">drawRoundedRect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">strokeStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">fillStyle</span><span class="o">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">x</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">radius</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">roundedRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">,</span> <span class="nx">radius</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">strokeStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">fillStyle</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 调用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">drawRoundedRect</span><span class="p">(</span><span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">86</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="图片加载为圆形">图片加载为圆形</h3> +<p>基本原理是,正常加载图片,canvas画个圆给它裁剪掉,上代码!</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">save</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 如果小伙伴儿调试时候感觉圆形和图片有点错位,可以开启下面两行注释代码,给圆圈加个边框 +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.setStrokeStyle(&#39;#AAAAAA&#39;) +</span></span></span><span class="line"><span class="cl"><span class="c1">// ctx.stroke() +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ctx</span><span class="p">.</span><span class="nx">clip</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">your</span><span class="o">-</span><span class="nx">image</span><span class="o">-</span><span class="nx">path</span><span class="o">&gt;</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">.</span><span class="nx">restore</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas生成的海报下载">canvas生成的海报下载</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">savePoster</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;确定保存到相册吗&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">canvasToTempFilePath</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">canvasId</span><span class="o">:</span> <span class="s1">&#39;sharePoster&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">saveImageToPhotosAlbum</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">filePath</span><span class="o">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">tempFilePath</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 此处为执行成功 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">openSetting</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">success</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">authSetting</span><span class="p">[</span><span class="s1">&#39;scope.writePhotosAlbum&#39;</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限成功,再次点击图片即可保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">uni</span><span class="p">.</span><span class="nx">showModal</span><span class="p">({</span> +</span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;提示&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;获取权限失败,无法保存&#39;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">showCancel</span><span class="o">:</span> <span class="kc">false</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fail</span><span class="o">:</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> <span class="k">this</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="问题">问题</h2> +<h3 id="图片有时显示有时不显示">图片有时显示有时不显示</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.加载图片”优化代码处,将加载图片全部写成同步的,在开始绘制前将图片全都加载好。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="base64数据的图片在小程序开发工具显示到了真机就不显示了">base64数据的图片在小程序开发工具显示,到了真机就不显示了</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">参照本文“二.3.3)base64”优化代码处,使用该方法即可。小程序canvas无法直接加载base64图片。 +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="canvas整体画成圆角的">canvas整体画成圆角的</h3> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">canvas背景是透明色,只要画个大小覆盖canvas的圆角矩形或者使用圆角背景图即可。 +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git "a/tags/\345\211\215\347\253\257/page/1/index.html" "b/tags/\345\211\215\347\253\257/page/1/index.html" new file mode 100644 index 0000000..c596d7a --- /dev/null +++ "b/tags/\345\211\215\347\253\257/page/1/index.html" @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/%E5%89%8D%E7%AB%AF/ + + + + + + diff --git "a/tags/\346\227\245\345\270\270\345\210\206\344\272\253/index.html" "b/tags/\346\227\245\345\270\270\345\210\206\344\272\253/index.html" new file mode 100644 index 0000000..56563b1 --- /dev/null +++ "b/tags/\346\227\245\345\270\270\345\210\206\344\272\253/index.html" @@ -0,0 +1,546 @@ + + + + +Tag: 日常分享 - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

日常分享

+ +
+
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git "a/tags/\346\227\245\345\270\270\345\210\206\344\272\253/index.xml" "b/tags/\346\227\245\345\270\270\345\210\206\344\272\253/index.xml" new file mode 100644 index 0000000..a0cac81 --- /dev/null +++ "b/tags/\346\227\245\345\270\270\345\210\206\344\272\253/index.xml" @@ -0,0 +1,68 @@ + + + + 日常分享 on 开发者小橙 + https://blog.hunterji.com/tags/%E6%97%A5%E5%B8%B8%E5%88%86%E4%BA%AB/ + Recent content in 日常分享 on 开发者小橙 + Hugo -- gohugo.io + en-us + Tue, 05 Dec 2023 15:56:29 +0000 + 程序员如何快速验证业务需求? + https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/ + Tue, 05 Dec 2023 15:56:29 +0000 + + https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/ + <img src="https://blog.hunterji.com/p/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E9%AA%8C%E8%AF%81%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82/cover.png" alt="Featured image of post 程序员如何快速验证业务需求?" /><h2 id="前言">前言</h2> +<p>设想一个场景:</p> +<p>你作为一个程序员,听着攒劲的小曲,写着优雅的代码,加班加点终于写完了迭代中分配的模块需求。然后你跟别的同事一联调,却发现各种问题。</p> +<p>你跟同事A说:“你得在这种情况下给我数据A。“</p> +<p>同事A一脸懵逼:“这种情况需求也没写啊,在这种情况下我也没有数据A啊!“</p> +<p>然后你和同事A一起顺着业务流程,找到了负责上层模块的同事B。</p> +<p>同事B一脸懵逼:“这个迭代中我没有这个模块的开发需求啊,这种情况下我也没有数据A啊!”</p> +<p>然后你和同事A、同事B一起顺着业务流程,找到了负责上层模块的&hellip;&hellip;</p> +<p>最后,你和同事ABC&hellip;一起找到了产品,产品一脸懵逼:“那没办法,就改需求吧&hellip;&hellip;”</p> +<p>你眼看着所剩无几的迭代时间,生无可恋地掏出手机,发个微信取消了这周末的相亲&hellip;</p> +<p>不知道程序员xdm有没有经历过这样的场景,如果各位程序员xd没有经历过这样的场景,那真的是恭喜你了,且行且珍惜!从毕业后参加工作开始,我是真的经历太多了,太痛了!</p> +<p>我之后不断学习各种方法,尝试去破解这样的困局。虽然说,工作中的问题很多,但是能解决一个是一个,后面再慢慢跟各位分享其他问题中我的解决方案。</p> +<p>所以,本篇文章将分享一波关于这类问题我的解决方案。谨代表个人主观观点。</p> +<h2 id="痛点">痛点</h2> +<p>需求是用户的痛点,但是在这个场景下,是程序员的痛点&hellip;</p> +<p>那么,仔细分析一下,这个场景下的痛点,到底是什么呢?我认为是如下两点:</p> +<ul> +<li>产品给到的需求无法自洽,且场景覆盖不够</li> +<li>开发团队的技术评审和技术文档不到位</li> +<li>开发各自按照模块开发,忽略外部环境,仅仅当各自开发完成后联调时候才会组装应用,发现问题</li> +</ul> +<p>当然,可能有程序员xd会疑问:这种问题不是靠严谨的开发流程就可以避免吗?</p> +<p>理论上来说是这样的,但是以我个人经历而言,不管是大团队还是小团队,总有很多不稳定的因素,比如说迭代周期、协作流程的规范性等等。</p> +<p>特别是在创业团队,或者有些项目特别着急的时候,经常是没有足够的时间给到团队的各个角色去充分准备的,甚至为了赶时间,会精简一些环节甚至直接去掉。这种时候出现这种问题导致改需求,往往是非常可怕的,后果也往往是开发团队加班。</p> +<h2 id="解决方案">解决方案</h2> +<p>我目前发现且实践后效果较好的方案,就是“曳光弹式开发”。</p> +<h3 id="什么是曳光弹">什么是曳光弹</h3> +<p>经常关注军事的小伙伴儿,应该了解,曳光弹是一种运用于军事场景的特殊子弹。</p> +<p>我们都知道武器发射时需要瞄准,比如枪就通过照门和准星来瞄准,而坦克和装甲车以及飞机都有专用的瞄准具,非常复杂。对于轻武器来说,一般攻击距离都比较近,使用自带的机械瞄准具或者外加的光学瞄准镜都足够,但是在距离稍远的时候瞄准镜显然不够。而对于飞机和战斗机来讲,在空中飞行时飞行姿态变化多样,<strong>有时候进攻的时间很短,需要快速射击并且调整弹道,这时候就需要可以发光的曳光弹。</strong></p> +<p>曳光弹正如其名,可以发光而且指示弹道。曳光弹的结构比一般子弹更加复杂。子弹的弹壳部分和一般子弹一样,前半部分是钢心或者铅心的弹头,但是在后部有一个空腔,一般称作曳光管,里面填充着曳光剂。曳光剂的成分还比较复杂,一般来说主要成分是镁粉和铝镁合金粉,用来燃烧,除此之外还有硝酸锶。这样一来,燃烧的时候硝酸锶就会发出红光。大家平时看到的很多曳光弹还有黄光和绿光,加入钠盐就会发出黄光,而加入铜盐就会发出绿光。除此之外还在表面加入一层过氧化钡,以保证曳光剂被点燃。</p> +<p>对于机枪手来说,如果射击距离较远,自然不能选择过于精确的设计方式,也就是说不能靠瞄准具来射击。而一般的机枪主要起压制和面杀伤作用,加入曳光弹就是机枪手更加快速方便的控制弹道,随时变化攻击的方向。如果没有曳光弹的话,射手根本无法发现自己的子弹弹道,也就很难去调整弹道。毕竟枪械射击温度升高之后,弹道会有所变化,不同的弹药不同的枪管也都会改变子弹的弹道,如果仅仅依靠枪械自身的瞄准具去调整反而会适得其反,而曳光弹就很好的解决了这个问题。</p> +<h3 id="在开发中的作用">在开发中的作用</h3> +<p>理解曳光弹本身在军事中的作用后,我们再来看曳光弹式开发如何在开发中起作用。</p> +<p>开发团队在接手到产品给到的业务需求后,特别是构建一些以前从未做过的东西时,对这个产品/功能的最终成效是模糊的。</p> +<p>程序员就像坦克上的机枪手一样,在尝试在黑暗中击中目标。但是如果像文章一开始的时候,大家先埋头写自己的模块,然后再联调,最终发现问题,感觉就像一上战场,大家都朝着各自理解的方向拼命清空弹夹,击中目标的寥寥无几,最后受伤的还是自己。</p> +<p>所以,此时,就需要先使用几颗曳光弹,去击中目标,在黑暗中划出轨迹,开发团队再调整方向,对着目标集中火力。</p> +<h3 id="如何应用">如何应用</h3> +<p>非常简单!步骤如下:</p> +<ol> +<li>找出级别最高的需求</li> +<li>以完成最基础的完整功能为目标,从前端到部署,开发一个可以运行的骨架</li> +<li>最后上相关需求的测试案例</li> +</ol> +<p>这样,射出一颗曳光弹,穿透客户端、后端、数据库、运维、测试等不同层面。一旦击中目标——即符合用户需求,后续的任务便大都是搬砖的活儿,去丰富这个骨架。</p> +<p>曳光弹并不是总能击中目标的,中途若是发现未能击中目标,便可在前期以极小成本去调整曳光弹的方向,继续发射曳光弹。</p> +<p>如果你要问,什么是“最基础的完整功能”,那么可以这么说,那么举个例子:前端不要任何样式,直接能够满足比如表单功能即可,后端不用任何校验,能够处理和传递数据即可。</p> +<h2 id="总结">总结</h2> +<p>为了解决文章开头的问题,需要使用曳光弹式开发,先开发一个骨架,确认满足需求后,即可继续丰富骨架,完成产品。</p> +<p>作为团队的一员,团队的每一个角色都很重要,程序员跟产品也是需要紧密协作,互帮互助,才能使得产品更好,也使得业绩更好。</p> + + + + + diff --git "a/tags/\346\227\245\345\270\270\345\210\206\344\272\253/page/1/index.html" "b/tags/\346\227\245\345\270\270\345\210\206\344\272\253/page/1/index.html" new file mode 100644 index 0000000..635b300 --- /dev/null +++ "b/tags/\346\227\245\345\270\270\345\210\206\344\272\253/page/1/index.html" @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/%E6%97%A5%E5%B8%B8%E5%88%86%E4%BA%AB/ + + + + + + diff --git "a/tags/\347\213\254\347\253\213\345\274\200\345\217\221/index.html" "b/tags/\347\213\254\347\253\213\345\274\200\345\217\221/index.html" new file mode 100644 index 0000000..8094d35 --- /dev/null +++ "b/tags/\347\213\254\347\253\213\345\274\200\345\217\221/index.html" @@ -0,0 +1,546 @@ + + + + +Tag: 独立开发 - 开发者小橙 + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

独立开发

+ +
+
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git "a/tags/\347\213\254\347\253\213\345\274\200\345\217\221/index.xml" "b/tags/\347\213\254\347\253\213\345\274\200\345\217\221/index.xml" new file mode 100644 index 0000000..86ca0f4 --- /dev/null +++ "b/tags/\347\213\254\347\253\213\345\274\200\345\217\221/index.xml" @@ -0,0 +1,166 @@ + + + + 独立开发 on 开发者小橙 + https://blog.hunterji.com/tags/%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91/ + Recent content in 独立开发 on 开发者小橙 + Hugo -- gohugo.io + en-us + Wed, 24 Jan 2024 23:53:17 +0000 + 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上) + https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/ + Wed, 24 Jan 2024 23:53:17 +0000 + + https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/ + <img src="https://blog.hunterji.com/p/%E8%A3%B8%E8%BE%9E%E5%90%8E%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91%E4%BA%A7%E5%93%81%E4%B8%8A%E7%BA%BF%E4%BA%94%E5%A4%A9%E5%BC%80%E5%A7%8B%E7%9B%88%E5%88%A9%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%81%9A%E7%9A%84%E5%88%86%E4%BA%AB%E6%88%91%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8A/cover.png" alt="Featured image of post 裸辞后独立开发产品上线五天开始盈利,我是怎么做的?分享我的过程(上)" /><h2 id="前言">前言</h2> +<p>裸辞后,我花了一个多月开发我的第一个独立开发产品<a class="link" href="https://redismate.hunterji.com/" target="_blank" rel="noopener" + >RedisMate</a>。其MVP版本上线五天,产品从没有排名,到冲到了国区mac app store开发工具类排名榜的前十,并持续有了几笔收入。</p> +<p>那么,我是怎么做的呢?</p> +<p>我是一个正在尝试独立开发的普通程序员,在此跟各位分享我的过程,不仅是我对自己的复盘,还希望能够给一些正在或准备尝试独立开发的兄弟一点参考。所有的过程都是按照我主观的想法去推进执行的,如果有更好的建议,欢迎交流!</p> +<p>本次分享分为上下两篇文章,分别为产品设计和产品推广两个主题。</p> +<h2 id="起源">起源</h2> +<p>首先,我并不是因为我有一个很棒的点子或产品,而选择去裸辞,去独立开发。</p> +<p>而是我因为别的原因选择了裸辞,然后开始把独立开发作为未来的一条路,并尝试去走。</p> +<p>所以,也可以说,一开始我是为了独立开发而去独立开发。</p> +<h2 id="产品选择">产品选择</h2> +<p>既然我没有一个很棒的点子去完成一个产品,那么我就开始去找。</p> +<h3 id="自身条件">自身条件</h3> +<p>首先,为了能够独立开发,我要审视自身,我会什么?</p> +<p>罗列下我的开发技能:</p> +<ul> +<li>前端:Vue、TS、&hellip;</li> +<li>后端:Go、Python、Rust</li> +<li>数据库:MySQL、Redis、PostgreSQL、ClickHouse、&hellip;</li> +<li>运维:Docker、MQ、Nginx、&hellip;</li> +<li>客户端/原生:Electron、Flutter、SwiftUI</li> +<li>硬件:esp32</li> +</ul> +<p>检查下其它独立开发必需技能:</p> +<ul> +<li>产品:自嗨水平</li> +<li>设计:自嗨水平</li> +<li>运营:几乎没有</li> +</ul> +<p>罗列下我熟悉的可以寻找需求的领域:</p> +<ul> +<li>开发</li> +<li>玩游戏</li> +<li>Linux日常办公</li> +<li>Mac日常办公</li> +</ul> +<p>我能在独立开发过程中投入多少资源:</p> +<ul> +<li>时间:全职</li> +<li>资金:起初的时候几乎完全不想投入一块钱,但起码要支付苹果个人开发者年费+服务器年费+域名年费</li> +</ul> +<h3 id="产品形态">产品形态</h3> +<p>那么,基于开发技能,得出我可以做的产品的形态:</p> +<ul> +<li>安卓app</li> +<li>ios app</li> +<li>Windows/Linux/macOS应用程序</li> +<li>SaaS</li> +<li>硬件</li> +<li>浏览器插件</li> +<li>编辑器插件</li> +<li>微信小程序</li> +</ul> +<p>但是考虑到希望以较小的成本来尝试独立开发,和希望能够短期得到收入来验证产品,那么就剩下了</p> +<ul> +<li>安卓app</li> +<li>ios app</li> +<li>Windows/Linux/macOS应用程序</li> +</ul> +<p>并且还都是离线应用,不需要服务器的支撑,没有太多服务器的额外成本,服务器仅仅需要最小配置用来备案即可。</p> +<p>考虑到付费方式的成本、上架的成本,再排除掉安卓app、Windows/Linux应用程序。</p> +<p>那么就只剩下</p> +<ul> +<li>ios app</li> +<li>macOS应用程序</li> +</ul> +<h3 id="产品需求">产品需求</h3> +<p>确定了产品形态,我就要考虑,到底要做什么产品呢?产品需求是什么?</p> +<p>如果我从当下很火但我完全不熟悉的领域去找需求,那我肯定没法挖掘真正的需求。</p> +<p>如果我找一个熟悉某个领域的人咨询,他并没有挖掘需求的能力,无法识别出真正的需求,这一点在我多年的工作中已经见识过太多太多次了,最后完完全全就是一个定制化项目,在市场上根本没有竞争力。</p> +<p>这就要从上述罗列的,我熟悉的领域去找。</p> +<p>所以我把目标放在了“开发”上。“开发”是我最熟悉的领域,在其中这么多年了,需求来源于我自己,我就是用户,我就可以直接验证这个需求是不是伪需求,这个功能是不是好功能。</p> +<p>并且,鉴于我三年前开发过一款开源的Redis GUI Client——<a class="link" href="https://github.com/hunter-ji/RedisFish" target="_blank" rel="noopener" + >RedisFish</a>,积累了一些经验,并且后来因为工作搁置了,导致一直没有完成也心有遗憾。</p> +<p>所以最终决定开发一款Redis GUI for Mac。</p> +<h2 id="市场调研">市场调研</h2> +<p>在决定产品之后,我先去问了一圈我的朋友和前同事,他们正在用的Redis GUI是什么。</p> +<p>得到的回复是如下产品:</p> +<ul> +<li>RedisDesktopManager</li> +<li>AnotherRedisDesktopManager</li> +<li>Medis2</li> +<li>RedisFish(被我安利用我自己的开源产品,哈哈&hellip;)</li> +</ul> +<p>其中RedisDesktopManager和AnotherRedisDesktopManager是最多的,毕竟是老牌的强劲产品!</p> +<p>然后,我又去翻了些论坛和帖子,发现有些人在发帖问mac上有哪些推荐的Redis GUI,但是哪怕是老外,也是如下选项居多:</p> +<ul> +<li>RedisDesktopManager</li> +<li>AnotherRedisDesktopManager</li> +<li>Medis2</li> +<li>RedisInsight</li> +</ul> +<p>接着,我也去了解了下近期新出的,比如Tiny RDM,听劝作品,真的很棒的,哈哈。</p> +<p>然后还有几个出现在比如gitee等平台的。</p> +<p>所以目前mac原生的也只有Medis2。</p> +<p>最后,我就要说那个经典台词了:没有能够满足我需求的!(不是&hellip;</p> +<h2 id="重新构思mvp版本">重新构思MVP版本</h2> +<p>到这个事件前,我已经开发完了既定的MVP版本,为v 1.1,此时的RedisMate是一个完成了基础Redis功能的App。</p> +<p>而我也已经怀着激动而又紧张的心情,在想如何推广MVP版本了。</p> +<p>但是,在我准备尝试推广的那个早上,在上厕所的时候,当时正在看《人性的弱点》。刚好看到书上讲了一个推销员的案例,推销员疯狂地讲述自己的产品怎么怎么好,让作者要赶紧买。</p> +<p>因此作者就在书上写道:</p> +<blockquote> +<p>人们其实什么东西都不需要。如果我们想买点什么,早就出门去买回来了。人们真正需要的,是解决问题的方式。人类永远都面临着种种问题,永远都需要这些问题的解决方案——如果销售人员能够证明其服务或产品可以帮助人们解决问题,不用推销,我们就会主动掏钱。对消费者而言,“主动买”比“被推销”的感觉好得多。</p> +</blockquote> +<p>所以,我就在想,我现在做的这是什么啊?!我能给其他人解决什么问题呢?凭什么要选择我的,而不是别的成熟产品?</p> +<p>我就开始重新构思MVP版本,希望能够加上独特的、创新的功能,能够真实解决用户问题的功能。</p> +<h2 id="创新">创新</h2> +<h3 id="寻找创新">寻找创新</h3> +<p>如何发现问题并解决问题?可以从经济学的四种不同类型的效用来入手:</p> +<ul> +<li>场所效益:使得原本无法接触的事物变得可接触</li> +<li>形式效用:通过重新排列现有部件使某物更有价值</li> +<li>时间工具:让事情变快的工具</li> +<li>拥有权利:去除中间人</li> +</ul> +<p>在此处,我的产品RedisMate上,最简单也是最快的的创新方式就是“让事情变得更快”。</p> +<p>我先明确Redis GUI使用频率最高的功能,然后从那个功能入手,让用户能够立竿见影感受到RedisMate的便捷。</p> +<p>那么,经过自己的总结,和跟朋友的沟通,我总结出,Redis GUI工具,使用频率从高到低的功能分别是:查(甚至是搜索)、删、改、增。</p> +<p>因此,我就从搜索功能入手。</p> +<p>我把RedisMate搜索功能的所有步骤罗列出来,然后思考,有哪些步骤可以减去?如何减少步骤?</p> +<p>最终就得到了现在“快速搜索”功能!</p> +<p>当Redis GUI窗口常驻时,一般最少需要约7步操作,那么在RedisMate上最少只要3步。</p> +<p>当Redis GUI仅打开没有连接任何服务器时,一般最少需要约9步操作,但是在RedisMate上最少只要3步。</p> +<p>当Redis GUI反复搜索固定key时,一般最少需要约4步,但是在RedisMate最少仅仅需要1步。</p> +<p>所以,我在RedisMate上针对搜索场景,进行了优化,并确实在推广时,得到了不少积极反馈。</p> +<p>并且,基于上述过程,优化了RedisMate的布局和一些细微的功能,让用户能够更直观更快去操作。</p> +<h3 id="挖掘需求">挖掘需求</h3> +<p>等我后面回过神的时候,才发现我一直有这个需求。</p> +<p>经常性,我要前后端一起开发,由于是微服务,多个节点并发开发和调试,每次都要开超多窗口。</p> +<p>我经常要切换窗口时,要愣一会儿思考一下,我要切什么窗口来着&hellip;</p> +<p>所以我就一直在想,能不能省略掉这些窗口,窗口自己呼出然后切回去!</p> +<p>现在RedisMate就可以了,这就解决了我曾经的问题。</p> +<p>然后,我就又想到曾经的一个抱怨,有时候在调试时,代码有点问题,或者业务链路较长,需要联调时间有点长,或者MQ驱动的业务节点产生key有点慢等等,key在我查的时候没有出现。我就要反复调出Redis GUI然后点点点后搜索再点点点,发现不对!调一下代码,再来一遍!</p> +<p>我当时就在想,要是GUI能帮我盯着就好了。</p> +<p>所以,我设计了&quot;Search Until Found&quot;功能,在得到朋友的肯定后,将其加入到了MVP版本中 。</p> +<h2 id="总结">总结</h2> +<p>首先,我很庆幸自己这些年学习和玩了不少技术,让我能够在想去独立开发的时候,不仅能够独立完成开发,还能有多种产品形态可以选择。并且我也有意识地去读技术之外的书,让我在其它领域有那么点点基础,只可惜没有学得更多。</p> +<p>其次,我觉得对我而言,苹果生态开发是当前成本最小的方式。</p> +<p>然后,我从我熟悉的领域入手,寻找自己的痛点,验证自己的需求,得到解决方案后,向身边的目标用户寻求验证。</p> +<p>最后,花一个多月开发完MVP版本,推出来验证自己的产品。</p> +<h2 id="最后">最后</h2> +<p>感谢你看完这篇文章,希望我的经过能够给你一些参考,帮助到你。</p> +<p>本次分享的下一篇文章——产品推广方面,我正在努力写,敬请期待并关注我以获取更新。</p> +<p>如果你对我的产品RedisMate感兴趣,非常欢迎<a class="link" href="https://redismate.hunterji.com/" target="_blank" rel="noopener" + >点击此处</a>了解和下载,或者在mac app store中搜索“RedisMate“。</p> +<p>最后,要感谢各位道上兄弟的关照,给了很多支持和反馈。</p> +<p>还要特别感谢那些购买高级版的兄弟,非常感谢你们的支持!</p> + + + + + diff --git "a/tags/\347\213\254\347\253\213\345\274\200\345\217\221/page/1/index.html" "b/tags/\347\213\254\347\253\213\345\274\200\345\217\221/page/1/index.html" new file mode 100644 index 0000000..4a1e699 --- /dev/null +++ "b/tags/\347\213\254\347\253\213\345\274\200\345\217\221/page/1/index.html" @@ -0,0 +1,10 @@ + + + + https://blog.hunterji.com/tags/%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91/ + + + + + + diff --git a/ts/main.js b/ts/main.js new file mode 100644 index 0000000..91ab333 --- /dev/null +++ b/ts/main.js @@ -0,0 +1,11 @@ +(()=>{var g=class e{galleryUID;items=[];constructor(t,r=1){if(window.PhotoSwipe==null||window.PhotoSwipeUI_Default==null){console.error("PhotoSwipe lib not loaded.");return}this.galleryUID=r,e.createGallery(t),this.loadItems(t),this.bindClick()}loadItems(t){this.items=[];let r=t.querySelectorAll("figure.gallery-image");for(let i of r){let n=i.querySelector("figcaption"),o=i.querySelector("img"),s={w:parseInt(o.getAttribute("width")),h:parseInt(o.getAttribute("height")),src:o.src,msrc:o.getAttribute("data-thumb")||o.src,el:i};n&&(s.title=n.innerHTML),this.items.push(s)}}static createGallery(t){let r=t.querySelectorAll("img.gallery-image");for(let o of Array.from(r)){let s=o.closest("p");if(!s||!t.contains(s)||(s.textContent.trim()==""&&s.classList.add("no-text"),!s.classList.contains("no-text")))continue;let d=o.parentElement.tagName=="A",m=o,a=document.createElement("figure");if(a.style.setProperty("flex-grow",o.getAttribute("data-flex-grow")||"1"),a.style.setProperty("flex-basis",o.getAttribute("data-flex-basis")||"0"),d&&(m=o.parentElement),m.parentElement.insertBefore(a,m),a.appendChild(m),o.hasAttribute("alt")){let l=document.createElement("figcaption");l.innerText=o.getAttribute("alt"),a.appendChild(l)}if(!d){a.className="gallery-image";let l=document.createElement("a");l.href=o.src,l.setAttribute("target","_blank"),o.parentNode.insertBefore(l,o),l.appendChild(o)}}let i=t.querySelectorAll("figure.gallery-image"),n=[];for(let o of i)n.length?o.previousElementSibling===n[n.length-1]?n.push(o):n.length&&(e.wrap(n),n=[o]):n=[o];n.length>0&&e.wrap(n)}static wrap(t){let r=document.createElement("div");r.className="gallery";let i=t[0].parentNode,n=t[0];i.insertBefore(r,n);for(let o of t)r.appendChild(o)}open(t){let r=document.querySelector(".pswp");new window.PhotoSwipe(r,window.PhotoSwipeUI_Default,this.items,{index:t,galleryUID:this.galleryUID,getThumbBoundsFn:n=>{let o=this.items[n].el.getElementsByTagName("img")[0],s=window.pageYOffset||document.documentElement.scrollTop,c=o.getBoundingClientRect();return{x:c.left,y:c.top+s,w:c.width}}}).init()}bindClick(){for(let[t,r]of this.items.entries())r.el.querySelector("a").addEventListener("click",n=>{n.preventDefault(),this.open(t)})}},b=g;var u={};if(localStorage.hasOwnProperty("StackColorsCache"))try{u=JSON.parse(localStorage.getItem("StackColorsCache"))}catch{u={}}async function S(e,t,r){if(!e)return await Vibrant.from(r).getPalette();if(!u.hasOwnProperty(e)||u[e].hash!==t){let i=await Vibrant.from(r).getPalette();u[e]={hash:t,Vibrant:{hex:i.Vibrant.hex,rgb:i.Vibrant.rgb,bodyTextColor:i.Vibrant.bodyTextColor},DarkMuted:{hex:i.DarkMuted.hex,rgb:i.DarkMuted.rgb,bodyTextColor:i.DarkMuted.bodyTextColor}},localStorage.setItem("StackColorsCache",JSON.stringify(u))}return u[e]}var D=(e,t=500)=>{e.classList.add("transiting"),e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=t+"ms",e.style.height=e.offsetHeight+"px",e.offsetHeight,e.style.overflow="hidden",e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0",window.setTimeout(()=>{e.classList.remove("show"),e.style.removeProperty("height"),e.style.removeProperty("padding-top"),e.style.removeProperty("padding-bottom"),e.style.removeProperty("margin-top"),e.style.removeProperty("margin-bottom"),e.style.removeProperty("overflow"),e.style.removeProperty("transition-duration"),e.style.removeProperty("transition-property"),e.classList.remove("transiting")},t)},q=(e,t=500)=>{e.classList.add("transiting"),e.style.removeProperty("display"),e.classList.add("show");let r=e.offsetHeight;e.style.overflow="hidden",e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0",e.offsetHeight,e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=t+"ms",e.style.height=r+"px",e.style.removeProperty("padding-top"),e.style.removeProperty("padding-bottom"),e.style.removeProperty("margin-top"),e.style.removeProperty("margin-bottom"),window.setTimeout(()=>{e.style.removeProperty("height"),e.style.removeProperty("overflow"),e.style.removeProperty("transition-duration"),e.style.removeProperty("transition-property"),e.classList.remove("transiting")},t)},B=(e,t=500)=>window.getComputedStyle(e).display==="none"?q(e,t):D(e,t);function v(){let e=document.getElementById("toggle-menu");e&&e.addEventListener("click",()=>{document.getElementById("main-menu").classList.contains("transiting")||(document.body.classList.toggle("show-menu"),B(document.getElementById("main-menu"),300),e.classList.toggle("is-active"))})}function N(e,t,r){var i=document.createElement(e);for(let n in t)if(n&&t.hasOwnProperty(n)){let o=t[n];n=="dangerouslySetInnerHTML"?i.innerHTML=o.__html:o===!0?i.setAttribute(n,n):o!==!1&&o!=null&&i.setAttribute(n,o.toString())}for(let n=2;n{this.isDark()?this.currentScheme="light":this.currentScheme="dark",this.setBodyClass(),this.currentScheme==this.systemPreferScheme&&(this.currentScheme="auto"),this.saveScheme()})}isDark(){return this.currentScheme=="dark"||this.currentScheme=="auto"&&this.systemPreferScheme=="dark"}dispatchEvent(t){let r=new CustomEvent("onColorSchemeChange",{detail:t});window.dispatchEvent(r)}setBodyClass(){this.isDark()?document.documentElement.dataset.scheme="dark":document.documentElement.dataset.scheme="light",this.dispatchEvent(document.documentElement.dataset.scheme)}getSavedScheme(){let t=localStorage.getItem(this.localStorageKey);return t=="light"||t=="dark"||t=="auto"?t:"auto"}bindMatchMedia(){window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",t=>{t.matches?this.systemPreferScheme="dark":this.systemPreferScheme="light",this.setBodyClass()})}},E=y;function p(e){let t;return()=>{t&&window.cancelAnimationFrame(t),t=window.requestAnimationFrame(()=>e())}}var O=".article-content h1[id], .article-content h2[id], .article-content h3[id], .article-content h4[id], .article-content h5[id], .article-content h6[id]",T="#TableOfContents",L="#TableOfContents li",k="active-class";function V(e,t){let r=e.querySelector("a").offsetHeight,i=e.offsetTop-t.offsetHeight/2+r/2-t.offsetTop;i<0&&(i=0),t.scrollTo({top:i,behavior:"smooth"})}function U(e){let t={};return e.forEach(r=>{let n=r.querySelector("a").getAttribute("href");n.startsWith("#")&&(t[n.slice(1)]=r)}),t}function C(e){let t=[];return e.forEach(r=>{t.push({id:r.id,offset:r.offsetTop})}),t.sort((r,i)=>r.offset-i.offset),t}function M(){let e=document.querySelectorAll(O);if(!e){console.warn("No header matched query",e);return}let t=document.querySelector(T);if(!t){console.warn("No toc matched query",T);return}let r=document.querySelectorAll(L);if(!r){console.warn("No navigation matched query",L);return}let i=C(e),n=!1;t.addEventListener("mouseenter",p(()=>n=!0)),t.addEventListener("mouseleave",p(()=>n=!1));let o,s=U(r);function c(){let m=document.documentElement.scrollTop||document.body.scrollTop,a;i.forEach(f=>{m>=f.offset-20&&(a=document.getElementById(f.id))});let l;a&&(l=s[a.id]),a&&!l?console.debug("No link found for section",a):l!==o&&(o&&o.classList.remove(k),l&&(l.classList.add(k),n||V(l,t)),o=l)}window.addEventListener("scroll",p(c));function d(){i=C(e),c()}window.addEventListener("resize",p(d))}var $="a[href]";function P(){document.querySelectorAll($).forEach(e=>{e.getAttribute("href").startsWith("#")&&e.addEventListener("click",r=>{r.preventDefault();let i=decodeURI(e.getAttribute("href").substring(1)),n=document.getElementById(i),o=n.getBoundingClientRect().top-document.documentElement.getBoundingClientRect().top;window.history.pushState({},"",e.getAttribute("href")),scrollTo({top:o,behavior:"smooth"})})})}var x={init:()=>{v();let e=document.querySelector(".article-content");e&&(new b(e),P(),M());let t=document.querySelector(".article-list--tile");t&&new IntersectionObserver(async(s,c)=>{s.forEach(d=>{if(!d.isIntersecting)return;c.unobserve(d.target),d.target.querySelectorAll("article.has-image").forEach(async a=>{let l=a.querySelector("img"),f=l.src,H=l.getAttribute("data-key"),I=l.getAttribute("data-hash"),A=a.querySelector(".article-details"),h=await S(H,I,f);A.style.background=` + linear-gradient(0deg, + rgba(${h.DarkMuted.rgb[0]}, ${h.DarkMuted.rgb[1]}, ${h.DarkMuted.rgb[2]}, 0.5) 0%, + rgba(${h.Vibrant.rgb[0]}, ${h.Vibrant.rgb[1]}, ${h.Vibrant.rgb[2]}, 0.75) 100%)`})})}).observe(t);let r=document.querySelectorAll(".article-content div.highlight"),i="Copy",n="Copied!";r.forEach(o=>{let s=document.createElement("button");s.innerHTML=i,s.classList.add("copyCodeButton"),o.appendChild(s);let c=o.querySelector("code[data-lang]");c&&s.addEventListener("click",()=>{navigator.clipboard.writeText(c.textContent).then(()=>{s.textContent=n,setTimeout(()=>{s.textContent=i},1e3)}).catch(d=>{alert(d),console.log("Something went wrong",d)})})}),new E(document.getElementById("dark-mode-toggle"))}};window.addEventListener("load",()=>{setTimeout(function(){x.init()},0)});window.Stack=x;window.createElement=w;})(); +/*! +* Hugo Theme Stack +* +* @author: Jimmy Cai +* @website: https://jimmycai.com +* @link: https://github.com/CaiJimmy/hugo-theme-stack +*/ diff --git a/ts/search.js b/ts/search.js new file mode 100644 index 0000000..e22d8ef --- /dev/null +++ b/ts/search.js @@ -0,0 +1 @@ +(()=>{var m={"&":"&","<":"<",">":">",'"':""","\u2026":"…"};function T(l){return m[l]||l}function d(l){return l.replace(/[&<>"]/g,T)}function w(l){return l.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&")}var g=class l{data;form;input;list;resultTitle;resultTitleTemplate;constructor({form:t,input:e,list:r,resultTitle:o,resultTitleTemplate:n}){this.form=t,this.input=e,this.list=r,this.resultTitle=o,this.resultTitleTemplate=n,this.handleQueryString(),this.bindQueryStringChange(),this.bindSearchForm()}static processMatches(t,e,r=!0,o=140,n=20){e.sort((a,s)=>a.start-s.start);let h=0,i=0,c=0,u=[];for(;hi?(u.push(`${d(t.substring(i,i+n))} [...] `),u.push(`${d(t.substring(a.start-n,a.start))}`),c+=n*2):(u.push(d(t.substring(i,a.start))),c+=a.start-i);let s=h+1,p=a.end;for(;s${d(t.substring(a.start,p))}`),c+=p-a.start,h=s,i=p,r&&c>o)break}if(i(i[h]=w(n),n.trim()!=="")).join("|"),"gi");for(let n of e){let h=[],i=[],c={...n,preview:"",matchCount:0},u=n.content.matchAll(o);for(let s of Array.from(u))i.push({start:s.index,end:s.index+s[0].length});let a=n.title.matchAll(o);for(let s of Array.from(a))h.push({start:s.index,end:s.index+s[0].length});h.length>0&&(c.title=l.processMatches(c.title,h,!1)),i.length>0?c.preview=l.processMatches(c.content,i):c.preview=d(c.content.substring(0,140)),c.matchCount=h.length+i.length,c.matchCount>0&&r.push(c)}return r.sort((n,h)=>h.matchCount-n.matchCount)}async doSearch(t){let e=performance.now(),r=await this.searchKeywords(t);this.clear();for(let n of r)this.list.append(l.render(n));let o=performance.now();this.resultTitle.innerText=this.generateResultTitle(r.length,((o-e)/1e3).toPrecision(1))}generateResultTitle(t,e){return this.resultTitleTemplate.replace("#PAGES_COUNT",t).replace("#TIME_SECONDS",e)}async getData(){if(!this.data){let t=this.form.dataset.json;this.data=await fetch(t).then(r=>r.json());let e=new DOMParser;for(let r of this.data)r.content=e.parseFromString(r.content,"text/html").body.innerText}return this.data}bindSearchForm(){let t="",e=r=>{r.preventDefault();let o=this.input.value.trim();if(l.updateQueryString(o,!0),o==="")return t="",this.clear();t!==o&&(t=o,this.doSearch(o.split(" ")))};this.input.addEventListener("input",e),this.input.addEventListener("compositionend",e)}clear(){this.list.innerHTML="",this.resultTitle.innerText=""}bindQueryStringChange(){window.addEventListener("popstate",t=>{this.handleQueryString()})}handleQueryString(){let e=new URL(window.location.toString()).searchParams.get("keyword");this.input.value=e,e?this.doSearch(e.split(" ")):this.clear()}static updateQueryString(t,e=!1){let r=new URL(window.location.toString());t===""?r.searchParams.delete("keyword"):r.searchParams.set("keyword",t),e?window.history.replaceState("","",r.toString()):window.history.pushState("","",r.toString())}static render(t){return createElement("article",null,createElement("a",{href:t.permalink},createElement("div",{class:"article-details"},createElement("h2",{class:"article-title",dangerouslySetInnerHTML:{__html:t.title}}),createElement("section",{class:"article-preview",dangerouslySetInnerHTML:{__html:t.preview}})),t.image&&createElement("div",{class:"article-image"},createElement("img",{src:t.image,loading:"lazy"}))))}};window.addEventListener("load",()=>{setTimeout(function(){let l=document.querySelector(".search-form"),t=l.querySelector("input"),e=document.querySelector(".search-result--list"),r=document.querySelector(".search-result--title");new g({form:l,input:t,list:e,resultTitle:r,resultTitleTemplate:window.searchResultTitleTemplate})},0)});var f=g;})();