<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>工程化 on Last Stand</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/</link><description>Recent content in 工程化 on Last Stand</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Thu, 23 Oct 2025 00:00:00 +0800</lastBuildDate><atom:link href="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/index.xml" rel="self" type="application/rss+xml"/><item><title>前端模块化</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-001/</link><pubDate>Sat, 05 Oct 2024 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-001/</guid><description>&lt;h2 id="一模块化的理解"&gt;一、模块化的理解&lt;/h2&gt;
&lt;h3 id="1什么是模块"&gt;1.什么是模块?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起&lt;/li&gt;
&lt;li&gt;块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2模块化的进化过程"&gt;2.模块化的进化过程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;全局function模式 : 将不同的功能封装成不同的全局函数&lt;/strong&gt;&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;编码: 将不同的功能封装成不同的全局函数&lt;/li&gt;
&lt;li&gt;问题: 污染全局命名空间, 容易引起命名冲突或数据不安全，而且模块成员之间看不出直接关系&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m1&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m2&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;namespace模式 : 简单对象封装&lt;/strong&gt;&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;作用: 减少了全局变量，解决命名冲突&lt;/li&gt;
&lt;li&gt;问题: 数据不安全(外部可以直接修改模块内部的数据)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;www.baidu.com&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`foo() &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;bar&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`bar() &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;other data&amp;#39;&lt;/span&gt; &lt;span style="color:#75715e"&gt;//能直接修改模块内部的数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;() &lt;span style="color:#75715e"&gt;// foo() other data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样的写法会暴露所有模块成员，内部状态可以被外部改写。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IIFE模式：匿名函数自调用(闭包)&lt;/strong&gt;&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;作用: 数据是私有的, 外部只能通过暴露的方法操作&lt;/li&gt;
&lt;li&gt;编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口&lt;/li&gt;
&lt;li&gt;问题: 如果当前这个模块依赖另一个模块怎么办?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;// index.html文件
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;module.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;bar&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;) &lt;span style="color:#75715e"&gt;//undefined 不能访问模块内部数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;xxxx&amp;#39;&lt;/span&gt; &lt;span style="color:#75715e"&gt;//不是修改的模块内部的data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;() &lt;span style="color:#75715e"&gt;//没有改变
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// module.js文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(window) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;www.baidu.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//操作数据的函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//用于暴露有函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`foo() &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bar&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//用于暴露有函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`bar() &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;otherFun&lt;/span&gt;() &lt;span style="color:#75715e"&gt;//内部调用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;otherFun&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//内部私有的函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;otherFun()&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//暴露行为
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; window.&lt;span style="color:#a6e22e"&gt;myModule&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;bar&lt;/span&gt; } &lt;span style="color:#75715e"&gt;//ES6写法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})(window)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后得到的结果：&lt;/p&gt;</description></item><item><title>Webpack与Vite</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-002/</link><pubDate>Sat, 05 Oct 2024 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-002/</guid><description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;vite比webpack快？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;vite比webpack简单？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;今天我们就来分析一下，到底有什么不一样的地方！&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="定位"&gt;定位&lt;/h2&gt;
&lt;p&gt;对比之前，我们先要搞懂，vite与webpack的定位以及关系才可以。&lt;/p&gt;
&lt;p&gt;那前端社区中常谈到的这些工具&lt;code&gt;webpack&lt;/code&gt;、&lt;code&gt;rollup&lt;/code&gt;、&lt;code&gt;parcel&lt;/code&gt;、&lt;code&gt;esbuild&lt;/code&gt;、&lt;code&gt;vite&lt;/code&gt;、&lt;code&gt;vue-cli&lt;/code&gt;、&lt;code&gt;create-react-app&lt;/code&gt;、&lt;code&gt;umi&lt;/code&gt;他们之间的关系是怎样的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;webpack&lt;/code&gt;、&lt;code&gt;rollup&lt;/code&gt;、&lt;code&gt;parcel&lt;/code&gt;、&lt;code&gt;esbuild&lt;/code&gt;都是打包工具，代码写好之后，我们需要对代码进行&lt;code&gt;压缩&lt;/code&gt;、&lt;code&gt;合并&lt;/code&gt;、&lt;code&gt;转换&lt;/code&gt;、&lt;code&gt;分割&lt;/code&gt;、&lt;code&gt;打包&lt;/code&gt;等操作，这些工作需要打包工具去完成。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vue-cli&lt;/code&gt;、&lt;code&gt;create-react-app&lt;/code&gt;、&lt;code&gt;umi&lt;/code&gt; 是基于&lt;code&gt;webpack&lt;/code&gt;的上层封装，通过简单的配置就可以快速创建出一个项目，把更多的时间放在业务开发上。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vite&lt;/code&gt;开发环境依赖&lt;code&gt;esbuild&lt;/code&gt;进行预构建，生产环境则依赖&lt;code&gt;rollup&lt;/code&gt;进行打包，并且充分利用了现代浏览器的特性，比如&lt;code&gt;http2&lt;/code&gt;、&lt;code&gt;ES module&lt;/code&gt;，&lt;code&gt;vite&lt;/code&gt;是站在众多巨人肩膀上的一个产物， 类似&lt;code&gt;webpack + webpack-dev-server&lt;/code&gt;的结合体，是一个非常棒的前端项目的构建工具。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="运行原理"&gt;运行原理&lt;/h2&gt;
&lt;p&gt;首先，我们从运行原理上分析一下，vite为什么比webpack快。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;webpack运行原理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-002/image-01.webp"&gt;&lt;/p&gt;
&lt;p&gt;当我们使用webpack启动项目时，webpack会根据我们配置文件（&lt;code&gt;webpack.config.js&lt;/code&gt;） 中的入口文件（&lt;code&gt;entry&lt;/code&gt;），分析出项目项目所有依赖关系，然后打包成一个文件（&lt;code&gt;bundle.js&lt;/code&gt;），交给浏览器去加载渲染。&lt;/p&gt;
&lt;p&gt;这样就会带来一个问题，项目越大，需要打包的东西越多，启动时间越长。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关于&lt;code&gt;ES module&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在讲vite运行原理之前，我们先说一下&lt;code&gt;ES module&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;目前，绝大多数现代浏览器都已经支持&lt;code&gt;ES module&lt;/code&gt;了， 我们只需要在&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签中添加&lt;code&gt;type=&amp;quot;module&amp;quot;&lt;/code&gt;，就可以使用&lt;code&gt;ES module&lt;/code&gt;了。&lt;/p&gt;
&lt;p&gt;下面这段代码是可以直接在浏览器中运行的。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// test.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;  &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;hello world&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;module&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;  &lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;./test.js&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;  &lt;span style="color:#a6e22e"&gt;hello&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// hello world
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/scirpt&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;vite运行原理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-002/image-02.webp"&gt;&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;&lt;/code&gt;中，浏览器遇到内部的import引用时，会自动发起http请求，去加载对应的模块。&lt;/p&gt;
&lt;p&gt;vite也正是利用了&lt;code&gt;ES module&lt;/code&gt;这个特性，使用vite运行项目时，首先会用&lt;code&gt;esbuild&lt;/code&gt;进行预构建，将所有模块转换为&lt;code&gt;es module&lt;/code&gt;，不需要对我们整个项目进行编译打包，而是在浏览器需要加载某个模块时，拦截浏览器发出的请求，根据请求进行&lt;code&gt;按需编译&lt;/code&gt;，然后返回给浏览器。&lt;/p&gt;
&lt;p&gt;这样一来，首次启动项目（冷启动）时，自然也就比webpack快很多了，并且项目大小对vite启动速度的影响也很小。&lt;/p&gt;
&lt;h2 id="构建方式"&gt;构建方式&lt;/h2&gt;
&lt;p&gt;我们再来看一下，vite与webpack在项目构建上有哪些区别。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;webpack&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;webpack是基于&lt;code&gt;nodejs&lt;/code&gt;运行的，但&lt;code&gt;js&lt;/code&gt;只能单线程运行，无法利用多核CPU的优势，当项目越来越大时，构建速度也就越来越慢了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;vite&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;vite&lt;code&gt;预构建&lt;/code&gt;与&lt;code&gt;按需编译&lt;/code&gt;的过程，都是使用&lt;code&gt;esbuild&lt;/code&gt;完成的。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;esbuild&lt;/code&gt;是用&lt;code&gt;go&lt;/code&gt;语言编写的，可以充分利用多核CPU的优势，所以vite开发环境下的&lt;code&gt;预构建&lt;/code&gt;与&lt;code&gt;按需编译&lt;/code&gt;速度，都是非常快的。&lt;/p&gt;
&lt;h2 id="http2"&gt;http2&lt;/h2&gt;
&lt;p&gt;vite充分利用了http2可以并发请求的优势，这也是速度快的一个主要原因。 接下来，我们了解一下&lt;strong&gt;http2&lt;/strong&gt;的来龙去脉。&lt;/p&gt;
&lt;p&gt;在之前http1的时候，浏览器对同一个域名的请求，是有并发限制的，一般为6个，如果并发请求6个以上，就会造成阻塞问题，所以在http1的时代，我们要减少打包产物的文件数量，减少并发请求，来提高项目的加载速度。&lt;/p&gt;
&lt;p&gt;2015年以后，http2出现了，他可以并发发送多个请求，不会出现http1的并发限制。这时候，将打包产物分成多个小模块，并行去加载，反而会更快。&lt;/p&gt;
&lt;p&gt;vite也充分利用了这一优势，对项目资源进行了合理的拆分，访问项目时，同时加载多个模块，来提升项目访问速度。&lt;/p&gt;
&lt;h2 id="热更新"&gt;热更新&lt;/h2&gt;
&lt;p&gt;vite速度快的另一个原因是与webpack不同的热更新机制。&lt;/p&gt;</description></item><item><title>任务自动化</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-003/</link><pubDate>Sat, 05 Oct 2024 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-003/</guid><description>&lt;p&gt;在前端开发中，我们经常需要执行各种重复性任务，比如编译代码、启动本地服务器、监听文件变化、运行测试、部署代码等。虽然可以使用不同的工具来完成这些任务，但&lt;code&gt;npm&lt;/code&gt;本身提供了一个强大的功能：&lt;code&gt;npm scripts&lt;/code&gt;。通过&lt;code&gt;npm scripts&lt;/code&gt;，我们可以将这些任务脚本化并集中管理，从而提高开发效率。本文将专注于如何使用&lt;code&gt;npm scripts&lt;/code&gt;自动化前端开发任务，以及它在项目管理中的作用。&lt;/p&gt;
&lt;h3 id="什么是npm-scripts"&gt;什么是&lt;code&gt;npm scripts&lt;/code&gt;？&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;npm scripts&lt;/code&gt;是&lt;code&gt;npm&lt;/code&gt;提供的一种机制，允许我们在项目的&lt;code&gt;package.json&lt;/code&gt;文件中定义一组命令，方便执行各种任务。在&lt;code&gt;package.json&lt;/code&gt;文件中，&lt;code&gt;scripts&lt;/code&gt;字段可以包含多个脚本，每个脚本都是一个键值对，其中键是脚本的名称，值是实际要执行的命令。例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;scripts&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;build&amp;#34;: &amp;#34;webpack --config webpack.config.js&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;test&amp;#34;: &amp;#34;jest&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上面的示例中，定义了两个脚本：&lt;code&gt;build&lt;/code&gt; 和 &lt;code&gt;test&lt;/code&gt;。可以通过以下方式在命令行中运行这些脚本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm run build
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm run test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;npm&lt;/code&gt;会执行对应的命令，自动帮我们完成编译和测试任务。&lt;/p&gt;
&lt;h3 id="为什么使用npm-scripts"&gt;为什么使用&lt;code&gt;npm scripts&lt;/code&gt;？&lt;/h3&gt;
&lt;p&gt;在现代前端开发流程中，自动化任务是不可或缺的，而&lt;code&gt;npm scripts&lt;/code&gt;的使用有以下几大优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;减少对全局依赖的需求&lt;/strong&gt;：在传统项目中，开发者通常需要全局安装各种命令行工具，如&lt;code&gt;webpack&lt;/code&gt;、&lt;code&gt;eslint&lt;/code&gt;、&lt;code&gt;babel&lt;/code&gt;等。而&lt;code&gt;npm scripts&lt;/code&gt;可以直接调用项目的本地依赖，无需全局安装。这确保了不同开发环境中的工具版本一致，减少了版本冲突和兼容性问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集中管理任务&lt;/strong&gt;：&lt;code&gt;npm scripts&lt;/code&gt;将所有任务定义集中在&lt;code&gt;package.json&lt;/code&gt;文件中，项目中的所有开发人员都可以直观地看到可用的任务列表，无需了解每个工具的详细使用方法。只需运行&lt;code&gt;npm run &amp;lt;task&amp;gt;&lt;/code&gt;，便可一键完成常见任务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支持跨平台执行&lt;/strong&gt;：&lt;code&gt;npm scripts&lt;/code&gt;中的命令可以跨平台使用，在Windows、macOS和Linux系统上表现一致，尤其适合团队协作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;便于组合和链式执行&lt;/strong&gt;：&lt;code&gt;npm&lt;/code&gt;允许通过逻辑操作符来组合脚本，实现复杂任务的自动化执行。例如可以通过&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;和&lt;code&gt;||&lt;/code&gt;来串联多个任务，使得多个步骤一次完成。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="如何定义和使用npm-scripts"&gt;如何定义和使用&lt;code&gt;npm scripts&lt;/code&gt;？&lt;/h3&gt;
&lt;p&gt;让我们通过具体的示例来看看&lt;code&gt;npm scripts&lt;/code&gt;如何自动化前端开发任务。&lt;/p&gt;
&lt;h4 id="1-设置基本的开发任务"&gt;1. 设置基本的开发任务&lt;/h4&gt;
&lt;p&gt;假设我们正在开发一个React项目，我们可以在&lt;code&gt;package.json&lt;/code&gt;中定义以下基本任务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;scripts&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;start&amp;#34;: &amp;#34;webpack serve --mode development&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;build&amp;#34;: &amp;#34;webpack --mode production&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;test&amp;#34;: &amp;#34;jest&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;lint&amp;#34;: &amp;#34;eslint src/**/*.js&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的脚本分别执行以下任务：&lt;/p&gt;</description></item><item><title>前端构建工具详解</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-004/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-004/</guid><description>&lt;p&gt;谈到构建工具，大家首先想到的肯定就是 &lt;code&gt;Webpack&lt;/code&gt; 以及现在最🔥的 &lt;code&gt;Vite&lt;/code&gt;。 &lt;code&gt;Webpack&lt;/code&gt;，功能强大，生态丰富，从面世到今天，一直是很受大家欢迎；&lt;code&gt;Vite&lt;/code&gt; 采用 &lt;code&gt;unbundle&lt;/code&gt; 构建模式，带来了极致的开发体验，给开发人员以新的选择。&lt;/p&gt;
&lt;p&gt;在这两个构建工具之外，还有其他的构建工具，如和 &lt;code&gt;Webpack&lt;/code&gt;、&lt;code&gt;Vite&lt;/code&gt; 类似的 &lt;code&gt;Rollup&lt;/code&gt;、&lt;code&gt;Parcel&lt;/code&gt;、&lt;code&gt;Esbuild&lt;/code&gt;，自动化构建工具 &lt;code&gt;grunt&lt;/code&gt;、&lt;code&gt;gulp&lt;/code&gt;，以及更加久远的 &lt;code&gt;YUI Tool&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这些工具的存在，构成了前端构建工具的发展史。&lt;/p&gt;
&lt;h4 id="yui-tool--ant"&gt;YUI Tool + Ant&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;YUI tool&lt;/code&gt; 是 07 年左右出现的一个构建工具，功能比较简单，用于压缩混淆 &lt;code&gt;css&lt;/code&gt; 和 &lt;code&gt;js&lt;/code&gt; 代码，需要配合 &lt;code&gt;java&lt;/code&gt; 的 &lt;code&gt;Ant&lt;/code&gt; 使用。&lt;/p&gt;
&lt;p&gt;当时 web 应用开发主要采用 &lt;code&gt;JSP&lt;/code&gt;，还不像现在这样前后端分离，通常是由 java 开发人员来编写 js、css 代码，前端代码都是和后端 java 代码放在一起的。因此前端代码的压缩混淆也就基于 java 实现了。&lt;/p&gt;
&lt;h4 id="grunt--gulp"&gt;Grunt / Gulp&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Grunt&lt;/code&gt; / &lt;code&gt;Gulp&lt;/code&gt; 都是运行在 &lt;code&gt;node&lt;/code&gt; 环境上的自动化工具。&lt;/p&gt;
&lt;p&gt;在开发过程中，我们可以将一些常见操作如&lt;code&gt;解析 html&lt;/code&gt;、&lt;code&gt;es6 代码转换为 es5&lt;/code&gt;、&lt;code&gt;less / sass 代码转换为 css 代码&lt;/code&gt;、&lt;code&gt;代码检查&lt;/code&gt;、&lt;code&gt;代码压缩&lt;/code&gt;、&lt;code&gt;代码混淆&lt;/code&gt;配置成一系列任务，然后通过 &lt;code&gt;Grunt&lt;/code&gt; / &lt;code&gt;Gulp&lt;/code&gt; 自动执行这些任务。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Grunt&lt;/code&gt; 和 &lt;code&gt;Gulp&lt;/code&gt; 的不同点：&lt;/p&gt;</description></item><item><title>前端任务自动化（npm scripts实现）</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-005/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-005/</guid><description>&lt;p&gt;在前端开发中，我们经常需要执行各种重复性任务，比如编译代码、启动本地服务器、监听文件变化、运行测试、部署代码等。虽然可以使用不同的工具来完成这些任务，但&lt;code&gt;npm&lt;/code&gt;本身提供了一个强大的功能：&lt;code&gt;npm scripts&lt;/code&gt;。通过&lt;code&gt;npm scripts&lt;/code&gt;，我们可以将这些任务脚本化并集中管理，从而提高开发效率。&lt;/p&gt;
&lt;p&gt;本文将专注于如何使用&lt;code&gt;npm scripts&lt;/code&gt;自动化前端开发任务，以及它在项目管理中的作用。&lt;/p&gt;
&lt;h3 id="什么是npm-scripts"&gt;什么是&lt;code&gt;npm scripts&lt;/code&gt;？&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;npm scripts&lt;/code&gt;是&lt;code&gt;npm&lt;/code&gt;提供的一种机制，允许我们在项目的&lt;code&gt;package.json&lt;/code&gt;文件中定义一组命令，方便执行各种任务。在&lt;code&gt;package.json&lt;/code&gt;文件中，&lt;code&gt;scripts&lt;/code&gt;字段可以包含多个脚本，每个脚本都是一个键值对，其中键是脚本的名称，值是实际要执行的命令。例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;scripts&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;build&amp;#34;: &amp;#34;webpack --config webpack.config.js&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;test&amp;#34;: &amp;#34;jest&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上面的示例中，定义了两个脚本：&lt;code&gt;build&lt;/code&gt; 和 &lt;code&gt;test&lt;/code&gt;。可以通过以下方式在命令行中运行这些脚本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm run build
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm run test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;npm&lt;/code&gt;会执行对应的命令，自动帮我们完成编译和测试任务。&lt;/p&gt;
&lt;h3 id="为什么使用npm-scripts"&gt;为什么使用&lt;code&gt;npm scripts&lt;/code&gt;？&lt;/h3&gt;
&lt;p&gt;在现代前端开发流程中，自动化任务是不可或缺的，而&lt;code&gt;npm scripts&lt;/code&gt;的使用有以下几大优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;减少对全局依赖的需求&lt;/strong&gt;：在传统项目中，开发者通常需要全局安装各种命令行工具，如&lt;code&gt;webpack&lt;/code&gt;、&lt;code&gt;eslint&lt;/code&gt;、&lt;code&gt;babel&lt;/code&gt;等。而&lt;code&gt;npm scripts&lt;/code&gt;可以直接调用项目的本地依赖，无需全局安装。这确保了不同开发环境中的工具版本一致，减少了版本冲突和兼容性问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集中管理任务&lt;/strong&gt;：&lt;code&gt;npm scripts&lt;/code&gt;将所有任务定义集中在&lt;code&gt;package.json&lt;/code&gt;文件中，项目中的所有开发人员都可以直观地看到可用的任务列表，无需了解每个工具的详细使用方法。只需运行&lt;code&gt;npm run &amp;lt;task&amp;gt;&lt;/code&gt;，便可一键完成常见任务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支持跨平台执行&lt;/strong&gt;：&lt;code&gt;npm scripts&lt;/code&gt;中的命令可以跨平台使用，在Windows、macOS和Linux系统上表现一致，尤其适合团队协作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;便于组合和链式执行&lt;/strong&gt;：&lt;code&gt;npm&lt;/code&gt;允许通过逻辑操作符来组合脚本，实现复杂任务的自动化执行。例如可以通过&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;和&lt;code&gt;||&lt;/code&gt;来串联多个任务，使得多个步骤一次完成。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="如何定义和使用npm-scripts"&gt;如何定义和使用&lt;code&gt;npm scripts&lt;/code&gt;？&lt;/h3&gt;
&lt;p&gt;让我们通过具体的示例来看看&lt;code&gt;npm scripts&lt;/code&gt;如何自动化前端开发任务。&lt;/p&gt;
&lt;h4 id="1-设置基本的开发任务"&gt;1. 设置基本的开发任务&lt;/h4&gt;
&lt;p&gt;假设我们正在开发一个React项目，我们可以在&lt;code&gt;package.json&lt;/code&gt;中定义以下基本任务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;scripts&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;start&amp;#34;: &amp;#34;webpack serve --mode development&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;build&amp;#34;: &amp;#34;webpack --mode production&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;test&amp;#34;: &amp;#34;jest&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;lint&amp;#34;: &amp;#34;eslint src/**/*.js&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的脚本分别执行以下任务：&lt;/p&gt;</description></item><item><title>代码规范工程化</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-006/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-006/</guid><description>&lt;p&gt;规范化是前端工程化的一个重要部分。现在，有许多工具能够辅助我们实行代码的规范化,比如你一定知道的 ESLint 和 Prettier。&lt;/p&gt;
&lt;p&gt;今天，来聊聊这些工具的工作原理和基本使用，了解它们是如何发挥作用的，以及如何更好地利用这些工具去规范项目的代码。&lt;/p&gt;
&lt;h2 id="1-eslint---检查你的-javascript-代码"&gt;1. ESlint - 检查你的 JavaScript 代码&lt;/h2&gt;
&lt;p&gt;让我们先从知名度最高的 ESLint 开始。&lt;/p&gt;
&lt;h3 id="11-eslint-及其作用"&gt;1.1. ESLint 及其作用&lt;/h3&gt;
&lt;p&gt;Lint 是一类专门用于检查代码的工具软件， 也称 linter。ESLint ，即 JavaScript （ECMAScript）代码的检查工具。&lt;/p&gt;
&lt;p&gt;正如官网的介绍 —— “Find and fix problems in your JavaScript code”，ESLint 能够辅助查找出你的 JavaScript 代码中的问题，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码风格问题（styles）。比如，运算符两边的空格、语句末尾的分号。&lt;/li&gt;
&lt;li&gt;不好的写法。比如，使用 &lt;code&gt;==&lt;/code&gt; 进行比较而不是 &lt;code&gt;===&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;可能存在逻辑问题的代码模式。比如，定义了一个变量，但没有使用到它。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，ESLint 还能够帮你自动修复一些简单的问题。&lt;/p&gt;
&lt;p&gt;我们将在下一小结学习如何使用 ESLint 检查我们的 JavaScript 代码，并修复其中的一些问题。&lt;/p&gt;
&lt;h3 id="12-eslint-快速上手"&gt;1.2. ESLint 快速上手&lt;/h3&gt;
&lt;p&gt;为了在项目中使用 ESLint，需要先安装它。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 初始化一个 npm 项目
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir eslint-test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd eslint-test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm init -y
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 安装 eslint
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm init @eslint/config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;回答一系列问题后，你可以看目录中的配置文件 &lt;code&gt;.eslintrc.js&lt;/code&gt;，这个配置文件告诉 ESLint 如何去解析项目，这个项目采用了哪些规范和规则。&lt;/p&gt;</description></item><item><title>版本控制</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/</guid><description>&lt;h2 id="什么是版本控制"&gt;什么是版本控制&lt;/h2&gt;
&lt;p&gt;版本控制是一种系统性的方法，用于管理和跟踪软件开发项目中的代码、文件和资源。它的核心目标是在不同时间点的开发中保持代码的一致性、可追溯性和可维护性。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-01.webp"&gt;&lt;/p&gt;
&lt;h2 id="为什么要做版本控制"&gt;为什么要做版本控制&lt;/h2&gt;
&lt;p&gt;这个问题可以从定义中的关键词可以看出端倪：不同时间点下的代码一致性、可追溯性、可维护性。这里额外补充一点：当今的互联网开发早已步入敏捷迭代，各种动态化方案层出不穷都有着顺应时代潮流的意思，因此软件开发一定会越来越多地涉及到团队开发，团队之间的协作也一定需要这样一个机制去保障稳定性。&lt;/p&gt;
&lt;h2 id="代码一致性"&gt;代码一致性&lt;/h2&gt;
&lt;p&gt;版本控制系统允许开发人员有效地管理和组织项目中的代码。它跟踪每个文件的历史记录，记录了每个版本的变更，包括谁做了什么修改、何时以及为什么。&lt;/p&gt;
&lt;h2 id="可追溯性"&gt;可追溯性&lt;/h2&gt;
&lt;p&gt;版本控制系统允许为重要的里程碑或发布创建标签（&lt;code&gt;tag&lt;/code&gt;），以便轻松识别和回滚到特定版本。&lt;/p&gt;
&lt;p&gt;并且当某个版本的代码出现问题，版本控制系统允许开发人员轻松地回滚到之前的稳定版本，并在不破坏其余代码的情况下进行修复。&lt;/p&gt;
&lt;h2 id="可维护性"&gt;可维护性&lt;/h2&gt;
&lt;p&gt;开发人员可以创建分支（&lt;code&gt;branch&lt;/code&gt;），这是一个独立的代码副本，用于开发新功能、修复错误或进行实验性工作。然后，这些分支可以与主代码库合并，以实现功能集成。&lt;/p&gt;
&lt;h2 id="团队协作"&gt;团队协作&lt;/h2&gt;
&lt;p&gt;在多人协作的环境中，不同开发者可能同时修改代码。版本控制系统确保不同的修改不会互相冲突，以及如何解决冲突。&lt;/p&gt;
&lt;p&gt;版本控制系统促进了团队之间的协作，可以通过代码审查工具进行代码审查，以确保代码质量和一致性。&lt;/p&gt;
&lt;h2 id="版本控制工具git"&gt;版本控制工具&lt;code&gt;Git&lt;/code&gt;&lt;/h2&gt;
&lt;h2 id="git的安装"&gt;&lt;code&gt;Git&lt;/code&gt;的安装&lt;/h2&gt;
&lt;h3 id="windows"&gt;&lt;code&gt;WIndows&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;访问 &lt;a href="https://link.juejin.cn/?target=https%3A%2F%2Fgit-scm.com%2Fdownload%2F"&gt;Git官网&lt;/a&gt;，选择对应的操作系统下载即可。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-02.webp"&gt;&lt;/p&gt;
&lt;p&gt;这里下载独立安装包即可&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-03.webp"&gt;&lt;/p&gt;
&lt;p&gt;点&lt;code&gt;install&lt;/code&gt;就行，会自动安装一个&lt;code&gt;git GUI&lt;/code&gt;工具，但通常不会直接用它&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-04.webp"&gt;&lt;/p&gt;
&lt;p&gt;下载完成安装即可，输入以下命令验证安装是否成功&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-05.webp"&gt;&lt;/p&gt;
&lt;h3 id="图形化工具"&gt;图形化工具&lt;/h3&gt;
&lt;h4 id="安装"&gt;安装&lt;/h4&gt;
&lt;p&gt;这里推荐&lt;a href="https://link.juejin.cn/?target=https%3A%2F%2Ftortoisegit.org%2F"&gt;TortoiseGit&lt;/a&gt;这个工具，非常方便。但我觉得Git本身学习成本并不高，&lt;code&gt;GUI&lt;/code&gt;工具更多是为了提升效率，基本的&lt;code&gt;Git&lt;/code&gt;知识点还是需要掌握的。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-06.webp"&gt;&lt;/p&gt;
&lt;p&gt;下载本体&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-07.webp"&gt;&lt;/p&gt;
&lt;p&gt;然后是语言包&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-08.webp"&gt;&lt;/p&gt;
&lt;p&gt;安装时一路&lt;code&gt;next&lt;/code&gt;即可，安装完本体后再安装语言包。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-09.webp"&gt;&lt;/p&gt;
&lt;p&gt;配置好&lt;code&gt;Github&lt;/code&gt;的账户、邮箱、秘钥等，注意配置好了密钥也不会在下述部分显示，可以打开编辑全局进行查看&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-10.webp"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-11.webp"&gt;&lt;/p&gt;
&lt;h4 id="使用"&gt;使用&lt;/h4&gt;
&lt;p&gt;在使用之前先提前补充一个点（后续会详细介绍），&lt;code&gt;Git&lt;/code&gt;有本地仓库和远端仓库的概念，通常支持&lt;code&gt;HTTPS&lt;/code&gt;和&lt;code&gt;SSH&lt;/code&gt;两种协议进行关联，两种方式优缺点也十分明显：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HTTPS&lt;/code&gt;：无需额外配置，但是在每次连接远端仓库时需要输入账号密码进行校验。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSH&lt;/code&gt;：生成一组密钥对，需要本地进行配置，好处是配置好后每次连接远端仓库时会自动比较密钥对，无需额外校验。关于&lt;code&gt;SSH&lt;/code&gt;详细可以参考这篇文章&lt;a href="https://juejin.cn/post/7111881008630202398"&gt;ssh-远程登录协议&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了一劳永逸，我们这里配置下&lt;code&gt;SSH&lt;/code&gt;，首先打开&lt;code&gt;Git bash&lt;/code&gt; &lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-12.webp"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 输入下列命令，最后一个参数是您的邮箱
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ssh-keygen -t rsa -C yourEmail.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到密钥对已经生成了&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-13.webp"&gt;&lt;/p&gt;
&lt;p&gt;进入上述目录，注意&lt;code&gt;.ssh&lt;/code&gt;文件夹默认是隐藏的，直接访问路径就行&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-14.webp"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-15.webp"&gt;&lt;/p&gt;
&lt;p&gt;以&lt;code&gt;Github&lt;/code&gt;为例，配置公钥&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-16.webp"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-17.webp"&gt;&lt;/p&gt;
&lt;p&gt;创建一个&lt;code&gt;Demo&lt;/code&gt;项目，平时学习的话就创建公有项目就行&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-18.webp"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-19.webp"&gt;&lt;/p&gt;
&lt;p&gt;复制一下我们的项目链接&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-007/image-20.webp"&gt;&lt;/p&gt;
&lt;p&gt;选取一个你喜欢的目录作为本地工作区，然后按鼠标右键&lt;/p&gt;</description></item><item><title>包管理</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-008/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-008/</guid><description>&lt;p&gt;现如今，前端开发的同学已经离不开 &lt;code&gt;npm&lt;/code&gt; 这个包管理工具，其优秀的包版本管理机制承载了整个繁荣发展的&lt;code&gt;NodeJS&lt;/code&gt;社区，理解其内部机制非常有利于加深我们对模块开发的理解、各项前端工程化的配置以加快我们排查问题（相信不少同学收到过各种依赖问题的困扰）的速度。&lt;/p&gt;
&lt;p&gt;本文从三个角度：&lt;code&gt;package.json&lt;/code&gt;、版本管理、依赖安装结合具体实例对 &lt;code&gt;npm&lt;/code&gt; 的包管理机制进行了详细分析。&lt;/p&gt;
&lt;h2 id="一剖析-packagejson"&gt;一、剖析 package.json&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-008/image-01.webp"&gt;&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;Node.js&lt;/code&gt; 中，模块是一个库或框架，也是一个 &lt;code&gt;Node.js&lt;/code&gt; 项目。&lt;code&gt;Node.js&lt;/code&gt; 项目遵循模块化的架构，当我们创建了一个 &lt;code&gt;Node.js&lt;/code&gt; 项目，意味着创建了一个模块，这个模块必须有一个描述文件，即 &lt;code&gt;package.json&lt;/code&gt;。它是我们最常见的配置文件，但是它里面的配置你真的有详细了解过吗？配置一个合理的 &lt;code&gt;package.json&lt;/code&gt; 文件直接决定着我们项目的质量，所以首先带大家分析下 &lt;code&gt;package.json&lt;/code&gt; 的各项详细配置。&lt;/p&gt;
&lt;h3 id="11-必备属性"&gt;1.1 必备属性&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; 中有非常多的属性，其中必须填写的只有两个：&lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;version&lt;/code&gt; ，这两个属性组成一个 &lt;code&gt;npm&lt;/code&gt; 模块的唯一标识。&lt;/p&gt;
&lt;h4 id="npm包命名规则"&gt;npm包命名规则&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt; 即模块名称，其命名时需要遵循官方的一些规范和建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;包名会成为模块&lt;code&gt;url&lt;/code&gt;、命令行中的一个参数或者一个文件夹名称，任何非&lt;code&gt;url&lt;/code&gt;安全的字符在包名中都不能使用，可以使用 &lt;code&gt;validate-npm-package-name&lt;/code&gt; 包来检测包名是否合法。&lt;/li&gt;
&lt;li&gt;语义化包名，可以帮助开发者更快的找到需要的包，并且避免意外获取错误的包。&lt;/li&gt;
&lt;li&gt;若包名称中存在一些符号，将符号去除后不得与现有包名重复&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如：由于&lt;code&gt;react-native&lt;/code&gt;已经存在，&lt;code&gt;react.native&lt;/code&gt;、&lt;code&gt;reactnative&lt;/code&gt;都不可以再创建。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果你的包名与现有的包名太相近导致你不能发布这个包，那么推荐将这个包发布到你的作用域下。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如：用户名 &lt;code&gt;conard&lt;/code&gt;，那么作用域为 &lt;code&gt;@conard&lt;/code&gt;，发布的包可以是&lt;code&gt;@conard/react&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id="查看包是否被占用"&gt;查看包是否被占用&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt; 是一个包的唯一标识，不得和其他包名重复，我们可以执行 &lt;code&gt;npm view packageName&lt;/code&gt; 查看包是否被占用，并可以查看它的一些基本信息：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-008/image-02.webp"&gt;&lt;/p&gt;
&lt;p&gt;若包名称从未被使用过，则会抛出 &lt;code&gt;404&lt;/code&gt; 错误：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-008/image-03.webp"&gt;&lt;/p&gt;
&lt;p&gt;另外，你还可以去 &lt;code&gt;https://www.npmjs.com/&lt;/code&gt; 查询更多更详细的包信息。&lt;/p&gt;
&lt;h3 id="12描述信息"&gt;1.2描述信息&lt;/h3&gt;
&lt;h4 id="基本描述"&gt;基本描述&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;description&amp;#34;: &amp;#34;An enterprise-class UI design language and React components implementation&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;keywords&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;ant&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;component&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;components&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;design&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;framework&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;frontend&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;react&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;react-component&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;ui&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;description&lt;/code&gt;用于添加模块的的描述信息，方便别人了解你的模块。&lt;/p&gt;</description></item><item><title>持续集成/持续部署（CI/CD）</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-009/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-009/</guid><description>&lt;h2 id="传统应用发布模式"&gt;传统应用发布模式&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;开发人员&lt;/strong&gt;：在开发环境完成代码编写，单元测试，测试通过后提交到代码仓库&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;运维人员&lt;/strong&gt;：把项目部署到测试环境，供QA团队测试，测试通过后，部署生成环境&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;测试人员&lt;/strong&gt;：进行测试，测试完成后通知运维部署生产环境&lt;/p&gt;
&lt;h3 id="缺点"&gt;缺点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;项目在早期就存在错误，但到最后集成的时候才发现&lt;/li&gt;
&lt;li&gt;需要手动操作，易错率高&lt;/li&gt;
&lt;li&gt;开发与运维需要及时沟通 有了以上缺点，那么就有了CI/CD&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="cicd"&gt;CI/CD&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;持续集成&lt;/em&gt;(CI):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;合并开发人员正在编写的所有代码&lt;/li&gt;
&lt;li&gt;一天内进行多次合并和提交代码&lt;/li&gt;
&lt;li&gt;从存储库或生产环境中进行构建和自动化测试，确保没有集成问题并及早发现任何问题&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;持续交付&lt;/em&gt;(CD):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;可以通过将更改自动推送到发布系统来随时将软件发布到生产环境中&lt;/li&gt;
&lt;li&gt;持续部署，并自动将更改推送到生产中&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="gitlab内置cicd"&gt;GitLab内置CI/CD&lt;/h2&gt;
&lt;h3 id="运行流水线任务"&gt;运行流水线任务&lt;/h3&gt;
&lt;p&gt;Job&lt;/p&gt;
&lt;p&gt;在文件中可以定义一个或多个作业，每个作业具有唯一的名称，每个作业是独立执行的，&lt;em&gt;每个作业至少包含一个script&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;stages：&lt;/p&gt;
&lt;p&gt;用于定义作业可以使用的阶段，并且是全局定义，同一个阶段的作业并行运行，不同阶段按顺序运行&lt;/p&gt;
&lt;p&gt;only：&lt;/p&gt;
&lt;p&gt;用分支策略来限制jobs构建&lt;/p&gt;
&lt;p&gt;script:&lt;/p&gt;
&lt;p&gt;项目中package中的脚本&lt;/p&gt;
&lt;p&gt;environment：&lt;/p&gt;
&lt;p&gt;定义此作业完成部署的环境名称&lt;/p&gt;
&lt;p&gt;下面是每个jobs的详细变量名&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Keyword&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;script&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;Runner执行的命令或脚本&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;image&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;所使用的docker镜像，查阅&lt;a href="https://docs.gitlab.com/ce/ci/docker/using_docker_images.html#define-image-and-services-from-gitlab-ciyml"&gt;使用docker镜像&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;services&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;所使用的docker服务，查阅&lt;a href="https://docs.gitlab.com/ce/ci/docker/using_docker_images.html#define-image-and-services-from-gitlab-ciyml"&gt;使用docker镜像&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stage&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义job stage（默认：&lt;code&gt;test&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;type&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stage&lt;/code&gt;的别名（已弃用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;variables&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义job级别的变量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;only&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义一列git分支，并为其创建job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;except&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义一列git分支，不创建job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tags&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义一列tags，用来指定选择哪个Runner（同时Runner也要设置tags）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;allow_failure&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;允许job失败。失败的job不影响commit状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;when&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义何时开始job。可以是&lt;code&gt;on_success&lt;/code&gt;，&lt;code&gt;on_failure&lt;/code&gt;，&lt;code&gt;always&lt;/code&gt;或者&lt;code&gt;manual&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dependencies&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义job依赖关系，这样他们就可以互相传递artifacts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cache&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义应在后续运行之间缓存的文件列表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;before_script&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;重写一组在作业前执行的命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;after_script&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;重写一组在作业后执行的命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;environment&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义此作业完成部署的环境名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;coverage&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;定义给定作业的代码覆盖率设置&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="配置gitlab-ciyml"&gt;配置.gitlab-ci.yml&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stages:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; deploy_dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; deploy_test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; deploy_production
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;deploy_dev:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage: deploy_dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; environment:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; only:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; script:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; rsync &lt;span style="color:#f92672"&gt;-&lt;/span&gt;av &lt;span style="color:#f92672"&gt;.&lt;/span&gt; &lt;span style="color:#f92672"&gt;$&lt;/span&gt;{WORKSPACE_PATH} &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cd &lt;span style="color:#f92672"&gt;$&lt;/span&gt;{WORKSPACE_PATH}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; npm run build:dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;playbook ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;deploy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;yml &lt;span style="color:#f92672"&gt;--&lt;/span&gt;extra&lt;span style="color:#f92672"&gt;-&lt;/span&gt;vars &lt;span style="color:#e6db74"&gt;&amp;#34;hosts=cloud_ui_test projectDir=${WORKSPACE_DIST_PATH} projectName=${PROJECT_NAME} webRootPath=/opt/devroot/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;deploy_test:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage: deploy_test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; environment:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; only:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; master
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; script:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; rsync &lt;span style="color:#f92672"&gt;-&lt;/span&gt;av &lt;span style="color:#f92672"&gt;.&lt;/span&gt; &lt;span style="color:#f92672"&gt;$&lt;/span&gt;{WORKSPACE_PATH} &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cd &lt;span style="color:#f92672"&gt;$&lt;/span&gt;{WORKSPACE_PATH}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; npm run build:stage
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;playbook ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;deploy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;yml &lt;span style="color:#f92672"&gt;--&lt;/span&gt;extra&lt;span style="color:#f92672"&gt;-&lt;/span&gt;vars &lt;span style="color:#e6db74"&gt;&amp;#34;hosts=cloud_ui_test projectDir=${WORKSPACE_DIST_PATH} projectName=${PROJECT_NAME} webRootPath=${WEB_ROOT_PATH}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;deploy_production:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; stage: deploy_production
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; only:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; production
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; when: manual
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; script:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; npm install
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; npm run build:prod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;playbook ansible&lt;span style="color:#f92672"&gt;-&lt;/span&gt;deploy&lt;span style="color:#f92672"&gt;.&lt;/span&gt;yml &lt;span style="color:#f92672"&gt;--&lt;/span&gt;extra&lt;span style="color:#f92672"&gt;-&lt;/span&gt;vars &lt;span style="color:#e6db74"&gt;&amp;#34;hosts=dvs-front-prod projectDir=${PROJECT_PATH} projectName=xianglin-cloud-ui/${PROJECT_NAME} webRootPath=/opt/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面这个.yml文件,我们首先定义了三个阶段，deploy_dev部署到dev环境，并且拉去的是dev分支代码; deploy_test部署到test环境，拉去的是master分支代码，deploy_production拉取production分支代码，执行完后需要手动操作&lt;/p&gt;</description></item><item><title>代码分割工程化</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-010/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-010/</guid><description>&lt;h2 id="什么是-bundle-splitting"&gt;什么是 Bundle Splitting？&lt;/h2&gt;
&lt;p&gt;Bundle Splitting（代码分割）是一种将前端资源拆分成更小、独立的模块的技术。传统上，前端资源（如 JavaScript、CSS 和图片等）被打包成单个文件，通常称为“bundle”。这种方式存在一个问题，即无论用户需要哪些功能，都必须下载整个 bundle。而 Bundle Splitting 技术通过将代码拆分为更小的模块，按需加载，可以有效解决这个问题。&lt;/p&gt;
&lt;h2 id="bundle-splitting-的优点"&gt;Bundle Splitting 的优点&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;减少初始加载时间 通过将代码拆分为多个小块，可以减少初始加载时间。当用户首次访问网页时，只需下载当前页面所需的资源，而无需等待整个应用程序的加载完成。这可以极大地提高页面的响应速度，并减少用户的等待时间。&lt;/li&gt;
&lt;li&gt;提升用户体验 通过将代码分割成模块，可以使应用程序更具响应性。当用户与应用进行交互时，只需加载所需的代码块，而不是整个应用。这将减少不必要的资源消耗，并使用户感觉应用更加流畅和快速。&lt;/li&gt;
&lt;li&gt;优化缓存策略 拆分代码还能优化缓存策略。当应用程序发生更改时，只需重新加载发生变化的模块，而不必重新下载整个应用。这减少了不必要的网络请求，提高了应用的更新效率，并减轻了服务器的负载。&lt;/li&gt;
&lt;li&gt;并行加载资源 通过将代码拆分为多个模块，可以实现并行加载资源。浏览器能够同时下载多个文件，从而减少整体加载时间。这对于大型应用程序和复杂的前端框架特别有益，能够更好地利用浏览器的并行下载能力，提高资源的加载效率。&lt;/li&gt;
&lt;li&gt;代码复用和维护 通过将代码分割成模块，可以更好地实现代码的复用和维护。不同的模块可以根据需求进行加载和升级，使得代码结构更加清晰、模块化。这也有助于团队合作，不同开发者可以并行工作在不同的模块上，提高开发效率和代码质量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="bundle-splitting-的缺点"&gt;Bundle Splitting 的缺点&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;增加了网络请求 拆分代码会增加应用程序的网络请求次数。每个模块都需要进行一次独立的网络请求，这可能会导致一些额外的延迟和性能损失。在网络条件较差的情况下，这可能会对用户体验产生一定影响。&lt;/li&gt;
&lt;li&gt;增加了开发复杂性 使用 Bundle Splitting 技术需要对应用程序的依赖关系进行深入分析和管理。开发人员需要仔细考虑代码的拆分点，以及如何在运行时动态加载模块。这增加了开发复杂性，并要求开发人员具备较高的技术水平和经验。&lt;/li&gt;
&lt;li&gt;潜在的代码冗余 当模块之间存在共享的代码片段时，可能会出现潜在的代码冗余问题。如果不合理地进行代码拆分，可能会导致多个模块中存在相同的代码，增加了资源的下载和维护成本。因此，在进行代码拆分时，需要仔细考虑代码复用和代码冗余的问题。&lt;/li&gt;
&lt;li&gt;需要额外的工具和配置 Bundle Splitting 需要使用额外的工具和配置来实现代码的拆分和动态加载。这可能需要对构建工具进行配置，如 Webpack、Rollup 等，以及对模块加载器进行调整。这增加了一些学习成本和开发成本，尤其是对于新手来说。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="bundle-splitting-的适用场景"&gt;Bundle Splitting 的适用场景&lt;/h2&gt;
&lt;p&gt;Bundle Splitting 技术适用于大型的 Web 应用程序，特别是那些具有复杂功能和大量依赖的应用。以下是一些适合使用 Bundle Splitting 的场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大型单页应用：对于由多个模块组成的单页应用，使用 Bundle Splitting 可以提高初始加载速度，提升用户体验。&lt;/li&gt;
&lt;li&gt;按需加载：对于需要动态加载不同功能模块的应用，Bundle Splitting 可以根据用户的需求按需加载模块，减少不必要的资源消耗。&lt;/li&gt;
&lt;li&gt;公共库和框架：对于使用公共库或框架的应用，可以将这些库和框架拆分成单独的模块，以便在多个页面中共享和复用。&lt;/li&gt;
&lt;li&gt;国际化支持：对于需要支持多种语言的应用，可以将不同语言的资源文件拆分成独立的模块，根据用户的语言偏好进行加载。&lt;/li&gt;
&lt;li&gt;代码分支管理：对于大型团队开发的项目，使用 Bundle Splitting 可以更好地实现代码的分支管理和并行开发，提高开发效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="bundle-splitting-在知名项目中的应用"&gt;Bundle Splitting 在知名项目中的应用&lt;/h2&gt;
&lt;h2 id="示例-1reactlazy-和-suspense"&gt;示例 1：React.lazy 和 Suspense&lt;/h2&gt;
&lt;p&gt;React.js 是一个广泛使用的 JavaScript 库，用于构建用户界面。React 提供了 React.lazy 和 Suspense API 来实现 Bundle Splitting。&lt;/p&gt;</description></item><item><title>前端性能监控</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-011/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-011/</guid><description>&lt;h2 id="大纲"&gt;大纲&lt;/h2&gt;
&lt;p&gt;我们会从以下三个方向来讲解埋点与监控的知识：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;什么是埋点？什么是监控？&lt;/li&gt;
&lt;li&gt;JS 中实现监控的核心方案&lt;/li&gt;
&lt;li&gt;写一个“相对”完整的监控实例&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一什么是埋点什么是监控"&gt;一、什么是埋点？什么是监控？&lt;/h2&gt;
&lt;p&gt;在日常沟通中，我们经常会把【埋点】和【监控】放到一起说，但是它们在本质上是有一定的区别的：&lt;/p&gt;
&lt;h3 id="1-埋点"&gt;1. 埋点&lt;/h3&gt;
&lt;p&gt;埋点主要用于&lt;strong&gt;收集用户行为数据&lt;/strong&gt;。在日常开发中，我们会通过 &lt;strong&gt;在前端代码中插入代码或脚本的方式&lt;/strong&gt; 来实现埋点功能。&lt;/p&gt;
&lt;p&gt;埋点的主要作用就是：&lt;strong&gt;捕获特定用户行为（如点击、浏览、提交表单、页面跳转等）以及关键业务数据（如下单金额、商品类别等）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在日常开发中，埋点的实现方案大致可以分为以下三大类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;手动埋点&lt;/strong&gt;：在代码中手动加入记录代码来捕获特定事件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动埋点&lt;/strong&gt;：利用 DOM 事件代理等技术来捕获页面上所有事件，从而减少手动配置。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可视化埋点&lt;/strong&gt;：通过工具界面标记需要采集的元素和事件，可以不用手写代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-监控"&gt;2. 监控&lt;/h3&gt;
&lt;p&gt;而监控则主要关注 &lt;strong&gt;系统的性能和稳定性&lt;/strong&gt;。在日常开发中，我们会通过 &lt;strong&gt;采集页面加载时间、资源请求、错误日志等数据&lt;/strong&gt; 的方式来实现前端监控。&lt;/p&gt;
&lt;p&gt;监控的主要作用就是：&lt;strong&gt;及时发现并定位页面性能瓶颈或代码异常，目的是为了保障系统不出 bug&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在日常开发中，监控一般需要完成以下三大部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;性能监控&lt;/strong&gt;：如：首屏加载时间、页面交互耗时、资源加载耗时等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;错误监控&lt;/strong&gt;：捕获 JavaScript 错误、网络请求失败、资源加载异常等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户体验监控&lt;/strong&gt;：收集白屏、卡顿等影响用户体验的问题等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="区别总结"&gt;区别总结&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;前端埋点&lt;/th&gt;
&lt;th&gt;前端监控&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;目标&lt;/td&gt;
&lt;td&gt;捕获用户行为数据&lt;/td&gt;
&lt;td&gt;监控系统性能、错误、稳定性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据类型&lt;/td&gt;
&lt;td&gt;用户点击、表单提交、页面跳转等&lt;/td&gt;
&lt;td&gt;页面加载时间、错误日志、卡顿情况等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;实现方式&lt;/td&gt;
&lt;td&gt;手动埋点、自动埋点、可视化埋点&lt;/td&gt;
&lt;td&gt;错误捕获、性能指标采集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;核心关注点&lt;/td&gt;
&lt;td&gt;用户行为、业务数据&lt;/td&gt;
&lt;td&gt;系统Bug、性能优化&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="二js-中实现监控的核心方案"&gt;二、JS 中实现监控的核心方案&lt;/h2&gt;
&lt;p&gt;根据上面所说，我们知道埋点和监控的目的存在不同，但是它们的思路确是有很多一致性的，其核心都是：&lt;strong&gt;获取关键的数据，发送（上报）给服务端&lt;/strong&gt;，依据数据来解决其不同的目的。&lt;/p&gt;
&lt;p&gt;所以，无论是埋点也好，还是监控也罢，我们都需要 &lt;strong&gt;获取关键位置数据&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="1-跟踪用户事件点击滚动等"&gt;1. 跟踪用户事件（点击、滚动等）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;定义通用跟踪函数（后续事件会通过该函数完成上报）&lt;/strong&gt;：&lt;code&gt;trackEvent&lt;/code&gt; 函数接收事件类型和事件详情，并上报到服务端。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 用于记录或发送跟踪数据到服务器的函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;trackEvent&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;eventType&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`Event: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;eventType&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 在控制台打印事件类型和详情
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 上报到服务端。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fetch&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/测试接口地址&amp;#39;&lt;/span&gt;, { &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;eventType&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt; }) });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;捕获按钮点击事件&lt;/strong&gt;：获取 &lt;code&gt;id&lt;/code&gt; 为 &lt;code&gt;myButton&lt;/code&gt; 的按钮，并在其 &lt;code&gt;click&lt;/code&gt; 事件上添加监听器。在按钮被点击时调用 &lt;code&gt;trackEvent&lt;/code&gt; 函数，记录点击事件的类型（&lt;code&gt;button_click&lt;/code&gt;）、按钮 ID 和时间戳。&lt;/p&gt;</description></item><item><title>前端组件化开发</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-012/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-012/</guid><description>&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;不知道你有没有遇到过以下场景:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页面逻辑越来越多,代码越来越庞大,写到后面难以 hold 住所有逻辑,很容易牵一发而动全身.&lt;/li&gt;
&lt;li&gt;你负责的页面好好的突然出现问题,查到最后是别人代码影响.&lt;/li&gt;
&lt;li&gt;同样的逻辑在多个地方重复书写,每次一改要改一批文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;随着前端项目复杂度的急剧增加, 上面列出的几种场景就是传统开发中会出现的问题. 也是前端组件化出现的原因.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;项目复杂度增加, 一个页面一个文件需要处理的内容过多.&lt;/li&gt;
&lt;li&gt;重复性劳动多, 效率低&lt;/li&gt;
&lt;li&gt;质量差, 不可控&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="组件化初探"&gt;组件化初探&lt;/h2&gt;
&lt;p&gt;正是由于出现了这样的问题, 为了在越来越复杂的前端项目中提高开发效率和保证开发质量, 各路大神们开始通过各种方式来尝试解决问题.&lt;/p&gt;
&lt;p&gt;曾经非常火的 jQuery 就基于自己建立了 jQuery 插件机制. 你可以将一些常用逻辑进行封装变成 jQuery 插件, 还可以将插件开源进行共享. 比如纯手写会吐血的日期时间选择器,轮播, 多级菜单等等. 你可以在&lt;a href="https://plugins.jquery.com/"&gt;这里&lt;/a&gt;浏览更多jQuery插件.&lt;/p&gt;
&lt;p&gt;jQuery 插件的用法通常是:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;.select&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;pluginName&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;除了 jQuery 插件模式, 还有一种常见模式是对象模式. 这种模式现在仍然有很多优秀的库在被我们直接或者间接使用. 比如: &lt;a href="https://swiperjs.com/"&gt;swiper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;对象模式的写法:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- Slider main container --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;swiper-container&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- Additional required wrapper --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;swiper-wrapper&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- Slides --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;swiper-slide&amp;#34;&lt;/span&gt;&amp;gt;Slide 1&amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;swiper-slide&amp;#34;&lt;/span&gt;&amp;gt;Slide 2&amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;swiper-slide&amp;#34;&lt;/span&gt;&amp;gt;Slide 3&amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;path/to/swiper.min.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mySwiper&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Swiper&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;.swiper-container&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;direction&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;vertical&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loop&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对象模式通过配置创建对象, 通常创建对象的参数中有一项是元素/元素选择器, 通过js代码将逻辑与这个元素紧密绑定.&lt;/p&gt;</description></item><item><title>单页面架构</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-013/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-013/</guid><description>&lt;h3 id="传统的多页面应用构建方式"&gt;传统的多页面应用构建方式：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;纯服务端渲染，前后端不分离，使用&lt;code&gt;jsp&lt;/code&gt;,&lt;code&gt;jade&lt;/code&gt;,&amp;rsquo;ejs&amp;rsquo;,&amp;rsquo;tempalte&amp;rsquo;等技术在后台先拼接成对应的&lt;code&gt;HTML&lt;/code&gt;结构，然后转换成字符串，在每个对应的路由返回对应的数据（文件）即可&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Jade&lt;/code&gt;模版服务端渲染，代码实现：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-mysql" data-lang="mysql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;const express&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;const app &lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;express&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;const jade &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;jade&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;const result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;***&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;const url path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;***&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;const html &lt;span style="color:#f92672"&gt;=&lt;/span&gt; jade.&lt;span style="color:#a6e22e"&gt;renderFile&lt;/span&gt;(url, &lt;span style="color:#960050;background-color:#1e0010"&gt;{&lt;/span&gt; data: result, urlPath &lt;span style="color:#960050;background-color:#1e0010"&gt;}&lt;/span&gt;)&lt;span style="color:#f92672"&gt;//&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;传入数据给模板引擎&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;,(req,res)&lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res.&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;(html)&lt;span style="color:#f92672"&gt;//&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;直接吐渲染好的&lt;/span&gt;&lt;span style="color:#f92672"&gt;`&lt;/span&gt;html&lt;span style="color:#f92672"&gt;`&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;文件拼接成字符串返回给客户端&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;}&lt;/span&gt;) &lt;span style="color:#f92672"&gt;//&lt;/span&gt;RestFul接口
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app.&lt;span style="color:#a6e22e"&gt;listen&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;,err&lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;//&lt;/span&gt;do something
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;}&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;jQuery&lt;/code&gt;等传统库绘制的前端页面&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="传统前后端不分离服务端渲染的优缺点"&gt;传统前后端不分离，服务端渲染的优缺点：&lt;/h3&gt;
&lt;h4 id="优点"&gt;优点：&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SEO&lt;/code&gt;友好，因为返回给前端的是渲染好的&lt;code&gt;HTML&lt;/code&gt;结构，里面的内容都可以被爬虫抓取到。&lt;/li&gt;
&lt;li&gt;对于一些应用性能等要求不高的项目，比如某个公司的静态网页，内容很少的情况下，直接一把梭就好，不用再搭建工程化的环境等&lt;/li&gt;
&lt;li&gt;对于后端程序员（全干工程师）来说，不用去特意学习前端框架，公司也不用特意去招聘前端&lt;/li&gt;
&lt;li&gt;兼容性好，传统服务端渲染多页面应用吐出来的都是字符串，&lt;code&gt;HTML&lt;/code&gt;结构&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="缺点"&gt;缺点：&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;如果项目很大，不利于维护，据我所知，目前很多云计算公司，还有不少都是使用非单页面应用，例如一个几十万行的项目是用&lt;code&gt;jQuery&lt;/code&gt;写的，如果注释和文档不是非常齐全，那么真的会无从下手&lt;/li&gt;
&lt;li&gt;性能和用户体验，不能跟单页面应用相比&lt;/li&gt;
&lt;li&gt;后期迭代，升级空间不大，目前大部分写得比较好的库，都建立&lt;code&gt;vue,react&lt;/code&gt;等框架基础上，他们都有一套自己的运行机制，有自己的生命周期，并且不像传统的应用，还加上了一层虚拟&lt;code&gt;DOM&lt;/code&gt;以及&lt;code&gt;diff&lt;/code&gt;算法&lt;/li&gt;
&lt;li&gt;现在类似&lt;code&gt;Ant-Design-pro&lt;/code&gt;这样的开箱即用的库已经很多，单页面应用的学习和开发成本已经很低很低，如果还在使用传统的技术去开发新的应用，对于开发人员多内心来说也是一种折磨。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这里并不是说多页面应用不好，只能说各有各自的好，单页面应用如果通过大量的极致优化手段，是可以从不少方面跟原生一拼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-013/image-01.webp"&gt;&lt;/p&gt;
&lt;h2 id="目前的单页面应用"&gt;目前的单页面应用：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;只有一张Web页面的应用，是一种从Web服务器加载的富客户端，单页面跳转仅刷新局部资源 ，公共资源(js、css等)仅需加载一次，常用于PC端官网、购物等网站&lt;/li&gt;
&lt;li&gt;其实只有一个空的&lt;code&gt;DIV&lt;/code&gt;标签，其他都是&lt;code&gt;js&lt;/code&gt;动态生态的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="单页面应用实现步骤"&gt;单页面应用实现步骤：&lt;/h3&gt;
&lt;h4 id="代码实现"&gt;代码实现：&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;首先是一个静态模板文件 &lt;code&gt;index.html&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;meta http-equiv=&amp;#34;X-UA-Compatible&amp;#34; content=&amp;#34;ie=edge&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;div id=&amp;#34;root&amp;#34;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;在&lt;code&gt;vue react&lt;/code&gt;框架的入口文件中指定对应的渲染元素：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import React from &amp;#39;react;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import ReactDOM from &amp;#39;react-dom&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ReactDOM.render(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;App/&amp;gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;document.querySelector(&amp;#34;#root&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;引入&lt;code&gt;react-router或者 react-router-dom，dva&lt;/code&gt;等路由跳转的库&lt;/li&gt;
&lt;li&gt;配置路由跳转&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;HashRouter&amp;gt;//这里使用HashRouter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;ErrorBoundary&amp;gt;//React错误边界
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Switch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Route path=&amp;#34;/login&amp;#34; component={Login} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Route path=&amp;#34;/home&amp;#34; component={Home} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;Route path=&amp;#34;/&amp;#34; component={NotFound} /&amp;gt;//404路由或者重定向都可以
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/Switch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/ErrorBoundary&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/HashRouter&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="单页面应用所谓路由跳转其实最终结果就是"&gt;单页面应用所谓路由跳转，其实最终结果就是：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;浏览器的&lt;code&gt;url&lt;/code&gt;地址发生变化，但是其实并没有发送请求，也没有刷新整个页面&lt;/li&gt;
&lt;li&gt;根据我们配置的路由信息，每次点击切换路由，会切换到不同的组件显示，类似于选项卡功能的实现,但是同时&lt;code&gt;url&lt;/code&gt;地址栏会变化&lt;/li&gt;
&lt;li&gt;分为&lt;code&gt;HashRouter&lt;/code&gt;和&lt;code&gt;BrowserRouter&lt;/code&gt;两种模式&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="自己实现一个粗略的路由跳转"&gt;自己实现一个粗略的路由跳转：&lt;/h2&gt;
&lt;h3 id="自己实现传统的hash模式跳转"&gt;自己实现传统的&lt;code&gt;Hash&lt;/code&gt;模式跳转：&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;hash 就是指 url 后的 # 号以及后面的字符。例如&lt;code&gt;www.baidu.com/#segmentfault&lt;/code&gt;,那么&lt;code&gt;#segmentfault&lt;/code&gt;就是&lt;code&gt;hash&lt;/code&gt;值&lt;/p&gt;</description></item><item><title>状态管理工程化</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-014/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-014/</guid><description>&lt;blockquote&gt;
&lt;p&gt;状态管理是一个前端界老生常谈的话题了，所有前端框架的发展历程中都离不开状态管理的迭代与更替，对于react来说呢，整个状态管理的发展也随着react架构的变更和新特性的加入而不停的做调整，作为一个一起伴随react成长了快5年的开发者，经历过reflux、redux、mobx，以及其他redux衍生方案dva、mirror、rematch等等后，我觉得它们都不是我想要的状态管理的终极形态，所以为了打造一个和react结合得最优雅、使用起来最简单、运行起来最高效的状态管理方案，踏上了追梦旅途。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="为何需要状态管理"&gt;为何需要状态管理&lt;/h2&gt;
&lt;p&gt;为何需要在前端引用里引入状态管理，基本上大家都达成了共识，在此我总结为3点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;随着应用的规模越来越大，功能越来越复杂，组件的抽象粒度会越来越细，在视图中组合起来后层级也会越来越深，能够方便的&lt;strong&gt;跨组件共享状态&lt;/strong&gt;成为迫切的需求。&lt;/li&gt;
&lt;li&gt;状态也需要按模块切分，状态的变更逻辑背后其实就是我们的业务逻辑，将其抽离出来能够彻底&lt;strong&gt;解耦ui和业务&lt;/strong&gt;，有利于逻辑复用，以及持续的维护和迭代。&lt;/li&gt;
&lt;li&gt;状态如果能够被集中的管理起来，并合理的派发有利于组件&lt;strong&gt;按需更新&lt;/strong&gt;，缩小渲染范围，从而提高渲染性能&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="已有状态管理方案现状"&gt;已有状态管理方案现状&lt;/h2&gt;
&lt;h3 id="redux"&gt;redux&lt;/h3&gt;
&lt;p&gt;遵循react不可变思路的状态管理方案，无论从git的star排名还是社区的繁荣度，首推的一定是&lt;code&gt;redux&lt;/code&gt;这个react界状态管理一哥，约束使用唯一路径&lt;code&gt;reducer&lt;/code&gt;纯函数去修改store的数据，从而达到整个应用的状态流转清晰、可追溯。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-014/image-01.png"&gt;&lt;/p&gt;
&lt;p&gt;image.png&lt;/p&gt;
&lt;h3 id="mbox"&gt;mbox&lt;/h3&gt;
&lt;p&gt;遵循响应式的后期之秀&lt;code&gt;mbox&lt;/code&gt;，提出了&lt;code&gt;computed&lt;/code&gt;、&lt;code&gt;reaction&lt;/code&gt;的概念，其官方的口号就是&lt;strong&gt;任何可以从应用程序状态派生的内容都应该派生出来&lt;/strong&gt;，通过将原始的普通json对象转变为可观察对象，我们可以直接修改状态，&lt;code&gt;mbox&lt;/code&gt;会自动驱动ui渲染更新，因其响应式的理念和&lt;code&gt;vue&lt;/code&gt;很相近，在&lt;code&gt;react&lt;/code&gt;里搭配&lt;code&gt;mobx-react&lt;/code&gt;使用后，很多人戏称&lt;code&gt;mobx&lt;/code&gt;是一个将&lt;code&gt;react&lt;/code&gt;变成了类&lt;code&gt;vue&lt;/code&gt;开发体验的状态管理方案。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-014/image-02.png"&gt;&lt;/p&gt;
&lt;p&gt;image.png&lt;/p&gt;
&lt;p&gt;当然因为&lt;code&gt;mbox&lt;/code&gt;操作数据很方便，不满足大型应用里对状态流转路径清晰可追溯的诉求，为了约束用户的更新行为，配套出了一个&lt;code&gt;mobx-state-tree&lt;/code&gt;，总而言之，&lt;code&gt;mobx&lt;/code&gt;成为了响应式的代表。&lt;/p&gt;
&lt;h3 id="其他"&gt;其他&lt;/h3&gt;
&lt;p&gt;剩下的状态管理方案，主要有3类。&lt;/p&gt;
&lt;p&gt;一类是不满足&lt;code&gt;redux&lt;/code&gt;代码冗余啰嗦，接口不够友好等缺点，进而在&lt;code&gt;redux&lt;/code&gt;之上做2次封装，典型的代表国外的有如&lt;code&gt;rematch&lt;/code&gt;，国内有如&lt;code&gt;dva&lt;/code&gt;、&lt;code&gt;mirror&lt;/code&gt;等，我将它们称为&lt;code&gt;redux&lt;/code&gt;衍生的家族作品，或者是解读了&lt;code&gt;redux&lt;/code&gt;源码，整合自己的思路重新设计一个库，如&lt;code&gt;final-state&lt;/code&gt;、&lt;code&gt;retalk&lt;/code&gt;、&lt;code&gt;hydux&lt;/code&gt;等，我将它们称为类&lt;code&gt;redux&lt;/code&gt;作品。&lt;/p&gt;
&lt;p&gt;一类是走响应式道路的方案，和&lt;code&gt;mobx&lt;/code&gt;一样，劫持普通状态对象转变为可观察对象，如&lt;code&gt;dob&lt;/code&gt;，我将它们称为类&lt;code&gt;mobx&lt;/code&gt;作品。&lt;/p&gt;
&lt;p&gt;剩下的就是利用&lt;code&gt;react context api&lt;/code&gt;或者最新的&lt;code&gt;hook&lt;/code&gt;特性，主打轻量，上手简单，概念少的方案，如&lt;code&gt;unstated-next&lt;/code&gt;，&lt;code&gt;reactn&lt;/code&gt;、&lt;code&gt;smox&lt;/code&gt;、&lt;code&gt;react-model&lt;/code&gt;等。&lt;/p&gt;
&lt;h2 id="我心中的理想方案"&gt;我心中的理想方案&lt;/h2&gt;
&lt;p&gt;上述相关的各种方案，都各自在一定程度上能满足我们的需求，但是对于追求完美的水瓶座程序猿，我觉得它们终究都不是我理想的方案，它们或小而美、或大而全，但还是不够强，不够友好，所以决定开始自研状态管理方案。&lt;/p&gt;
&lt;p&gt;我知道小和 美、全、强本身是相冲突的，我能接受一定量的大，gzip后10kb到20kb都是我接受的范围，在此基础上，去逐步地实现美、全、强，以便达到以下目的，从而体现出和现有状态管理框架的差异性、优越性。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让新手使用的时候，无需了解新的特性api，无感知状态管理的存在，使其遁于无形之中，仅按照react的思路组织代码，就能享受到状态管理带来的福利。&lt;/li&gt;
&lt;li&gt;让老手可以结合对状态管理的已有认知来使用新提供的特性api，还原各种社区公认的最佳实践，同时还能向上继续探索和提炼，挖掘状态管理带来的更多收益。&lt;/li&gt;
&lt;li&gt;在&lt;code&gt;react&lt;/code&gt;有了&lt;code&gt;hook&lt;/code&gt;特性之后，让class组件和function组件都能够享有一致的思路、一致的api接入状态管理，不产生割裂感。&lt;/li&gt;
&lt;li&gt;在保持以上3点的基础上，让用户能够使用更精简且更符合思维直觉的组织方式书写代码，同时还能够获得巨大的性能提升收益。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了达成以上目标，立项&lt;code&gt;concent&lt;/code&gt;，将其定义为一个&lt;strong&gt;可预测、零入侵、渐进式、高性能的增强型状态管理方案&lt;/strong&gt;，期待能把他打磨成为一个真真实实让用户用起来感觉到美丽、全面、强大的框架。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;说人话就是：理解起来够简单、代码写起来够优雅、工程架构起来够健壮、性能用起来够卓越&amp;hellip;&amp;hellip; ^_^&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-014/image-03.png"&gt;&lt;/p&gt;
&lt;p&gt;concent.png&lt;/p&gt;
&lt;h3 id="可预测"&gt;可预测&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;react&lt;/code&gt;是一个基于&lt;code&gt;pull based&lt;/code&gt;来做变化侦测的ui框架，对于用户来说，需要显式的调用&lt;code&gt;setState&lt;/code&gt;来让&lt;code&gt;react&lt;/code&gt;感知到状态变化，所以&lt;code&gt;concent&lt;/code&gt;遵循react经典的不可变原则来体现可预测，不使用劫持对象将转变为可观察对象的方式来感知状态变化（要不然又成为了一个类&lt;code&gt;mobx&lt;/code&gt;&amp;hellip;&amp;hellip;）, 也不使用时全局&lt;code&gt;pub&amp;amp;sub&lt;/code&gt;的模式来驱动相关视图更新，同时还要配置各种&lt;code&gt;reselect&lt;/code&gt;、&lt;code&gt;redux-saga&lt;/code&gt;等&lt;a href="https://cloud.tencent.com/product/message-queue-catalog?from_column=20065&amp;amp;from=20065"&gt;中间件&lt;/a&gt;来解决计算缓存、异步action等等问题（如果这样，岂不是又迈向了一个redux全家桶轮子的不归路&amp;hellip;.. ）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;吐槽一下：redux粗放的订阅粒度在组件越来越多，状态越来越复杂的时候，经常因为组件订阅了不需要的数据而造成冗余更新，而且各种手写mapXXXToYYY很烦啊有木有啊有木有，伤不起啊伤不起&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="零入侵"&gt;零入侵&lt;/h3&gt;
&lt;p&gt;上面提到了期望新手仅按照react的思路组织代码，就能够享受到状态管理带来的福利，所以必然只能在&lt;code&gt;setState&lt;/code&gt;之上做文章，其实我们可以把&lt;code&gt;setState&lt;/code&gt;当做一个下达渲染指令重要入口（除此之外，还有&lt;code&gt;forceUpdate&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/engineering/engineering-014/image-04.png"&gt;&lt;/p&gt;
&lt;p&gt;setState，下达更新指令&lt;/p&gt;
&lt;p&gt;仔细看看上图，有没有发现有什么描述不太准确的地方，我们看看官方的&lt;code&gt;setState&lt;/code&gt;函数签名描述:&lt;/p&gt;
&lt;p&gt;代码语言：txt&lt;/p&gt;
&lt;p&gt;AI代码解释&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;setState&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;K &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; keyof S&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; state: ((prevState: Readonly&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;S&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, props: Readonly&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;P&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; (Pick&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;S, K&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; S &lt;span style="color:#f92672"&gt;|&lt;/span&gt; null)) &lt;span style="color:#f92672"&gt;|&lt;/span&gt; (Pick&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;S, K&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; S &lt;span style="color:#f92672"&gt;|&lt;/span&gt; null),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; callback&lt;span style="color:#960050;background-color:#1e0010"&gt;?&lt;/span&gt;: () &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; void
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;): void;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过签名描述，我们可以看出传递给&lt;code&gt;setState&lt;/code&gt;的是一个部分状态(片段状态)，实际上我们在调用&lt;code&gt;setState&lt;/code&gt;也是经常这么做的，修改了谁就传递对应的&lt;code&gt;stateKey&lt;/code&gt;和值。&lt;/p&gt;</description></item></channel></rss>