<?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/tags/%E5%B7%A5%E7%A8%8B%E5%8C%96/</link><description>Recent content in 工程化 on Last Stand</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sun, 31 May 2026 09:00:00 +0800</lastBuildDate><atom:link href="https://amatsuzero.github.io/LastStand/tags/%E5%B7%A5%E7%A8%8B%E5%8C%96/index.xml" rel="self" type="application/rss+xml"/><item><title>编译优化-Bazel方案</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/bazel%E6%96%B9%E6%A1%88/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/bazel%E6%96%B9%E6%A1%88/</guid><description>&lt;p&gt;当 iOS 工程的规模突破 CocoaPods / Xcode 原生构建体系的舒适区，Bazel 就成为下一代构建系统的首选。字节跳动的头条、抖音、Airbnb、Uber、Lyft、Pinterest、Square、Bilibili 等都已把 iOS 工程迁到 Bazel。本文介绍 Bazel 的核心原理、在 iOS 上的生态（rules_apple / rules_swift / rules_xcodeproj）以及大厂落地实践。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="为什么是-bazel"&gt;为什么是 Bazel&lt;/h2&gt;
&lt;p&gt;CocoaPods + Xcode 在超大工程上的固有瓶颈：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;粗粒度依赖&lt;/strong&gt;：以 Pod / Target 为单位，增量编译颗粒粗&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隐式依赖&lt;/strong&gt;：Build Phases 隐含顺序，难以沙箱化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;难以远程缓存&lt;/strong&gt;：编译环境非 hermetic，hash 易变&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;难以远程执行&lt;/strong&gt;：Xcode Build System 绑定本地 macOS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bazel 针对这些问题从设计之初就提供了：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;能力&lt;/th&gt;
&lt;th&gt;Bazel&lt;/th&gt;
&lt;th&gt;Xcode/CocoaPods&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;Target 级&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;Sandbox&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;需第三方（ccache/Rugby）&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;一流（Swift/OC/C++/Go/Rust/JS）&lt;/td&gt;
&lt;td&gt;主要 Apple 平台&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;多平台&lt;/td&gt;
&lt;td&gt;天生&lt;/td&gt;
&lt;td&gt;iOS/macOS 为主&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="bazel-核心概念"&gt;Bazel 核心概念&lt;/h2&gt;
&lt;h3 id="workspace-与-package"&gt;Workspace 与 Package&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WORKSPACE # 仓库根，声明外部依赖
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;foo/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── BUILD.bazel # package，本目录的构建声明
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── main.swift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── util/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └── BUILD.bazel # 子 package
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Workspace&lt;/strong&gt;：整个 Bazel 仓库，一个 WORKSPACE 文件一个仓&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Package&lt;/strong&gt;：任何包含 &lt;code&gt;BUILD.bazel&lt;/code&gt; 的目录&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target&lt;/strong&gt;：BUILD 文件里的一个 rule 调用，比如 &lt;code&gt;swift_library(name = &amp;quot;foo&amp;quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Label&lt;/strong&gt;：Target 的全局 ID，形如 &lt;code&gt;//foo/util:util&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="rule"&gt;Rule&lt;/h3&gt;
&lt;p&gt;Rule 是构建函数，输入 sources + deps，输出 artifacts：&lt;/p&gt;</description></item><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>编译优化-CocoaPods优化</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/cocoapods%E4%BC%98%E5%8C%96/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/cocoapods%E4%BC%98%E5%8C%96/</guid><description>&lt;p&gt;在 iOS 生态中，CocoaPods 依然是大多数中大型工程的依赖管理工具。当 Pod 数量达到几百个子组件上千个时，&lt;code&gt;pod install&lt;/code&gt; 的耗时会变成研发流程里的硬性卡点。抖音在其 &lt;code&gt;seer-optimize&lt;/code&gt; 项目中系统化地优化了 CocoaPods 的整个生命周期，全量 Pod Install 耗时减少 50%、增量减少 65%。本文系统介绍这些优化背后的原理。&lt;/p&gt;
&lt;p&gt;关于 CocoaPods 整体架构的深度介绍可以参考 &lt;a href="https://amatsuzero.github.io/LastStand/posts/interview/ios-source-analysis/cocoapods-%E6%9E%B6%E6%9E%84%E6%80%BB%E8%A7%88/"&gt;CocoaPods源码导读-架构总览&lt;/a&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="pod-install-耗时构成"&gt;Pod Install 耗时构成&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Installer#install!&lt;/code&gt; 的六个阶段：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart LR
A[prepare] --&gt; B[resolve_dependencies]
B --&gt; C[download_dependencies]
C --&gt; D[validate_targets]
D --&gt; E[generate_pods_project]
E --&gt; F[integrate_user_project]
&lt;/pre&gt;
&lt;p&gt;抖音公开的典型耗时分布：&lt;/p&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;prepare&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.01s&lt;/td&gt;
&lt;td&gt;初始化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;resolve_dependencies&lt;/strong&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;strong&gt;download_dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;数秒到数分钟&lt;/td&gt;
&lt;td&gt;依赖下载，IO 密集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;validate_targets&lt;/td&gt;
&lt;td&gt;0.1s&lt;/td&gt;
&lt;td&gt;校验&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;generate_pods_project&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;数秒到数十秒&lt;/td&gt;
&lt;td&gt;生成 xcodeproj&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;integrate_user_project&lt;/td&gt;
&lt;td&gt;0.01s&lt;/td&gt;
&lt;td&gt;集成主工程&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="resolve_dependencies-优化"&gt;resolve_dependencies 优化&lt;/h2&gt;
&lt;h3 id="source-仓库更新"&gt;Source 仓库更新&lt;/h3&gt;
&lt;p&gt;默认 CocoaPods 在 &lt;code&gt;pod install --repo-update&lt;/code&gt; 时会更新&lt;strong&gt;所有&lt;/strong&gt; source 仓库。抖音改造为：&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>编译优化-Explicit Modules</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/explicit-modules/</link><pubDate>Tue, 05 May 2026 00:41:41 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/explicit-modules/</guid><description>&lt;p&gt;Apple 从 Xcode 15 开始在 Swift 上引入 Explicit Modules，Xcode 16 在 C/C++/Objective-C 上全面铺开，Xcode 17 进一步默认启用并与细粒度依赖追踪结合。这是近五年 Apple 构建系统最重要的一次变革，直接影响到大部分项目的编译模型。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="模块module基础"&gt;模块（Module）基础&lt;/h2&gt;
&lt;h3 id="为什么需要模块"&gt;为什么需要模块&lt;/h3&gt;
&lt;p&gt;C 家族语言的 &lt;code&gt;#include&lt;/code&gt; 机制有两个致命问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;文本替换&lt;/strong&gt;：头文件是纯文本替换，每个源文件都要重新解析一遍所有包含的头文件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;宏污染&lt;/strong&gt;：先引入的头文件宏会影响后续头文件的行为，没有隔离&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Clang 在 2012 年引入 &lt;strong&gt;Clang Modules&lt;/strong&gt;，用一个预编译的二进制模块（&lt;code&gt;.pcm&lt;/code&gt;）替代文本包含，同一 module 在一次构建里只解析一次。Swift 的 &lt;code&gt;.swiftmodule&lt;/code&gt; 从设计之初就是模块化的。&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart LR
A["源文件"] --&gt;|"include 头文件"| B["Foundation.h 文本"];
B --&gt; C["每次都重新解析"];
D["源文件"] --&gt;|"@import Foundation"| E["Foundation.pcm 预编译"];
E --&gt; F["一次解析，多次复用"];
&lt;/pre&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;Clang Module&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.pcm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;C/OC 模块的序列化 AST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swift Module&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.swiftmodule&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swift 模块的接口 + SIL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swift Interface&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.swiftinterface&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;可被不同 Swift 版本解析的文本接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module Map&lt;/td&gt;
&lt;td&gt;&lt;code&gt;module.modulemap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;描述哪些头文件构成某个 module&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="implicit-modules-的问题"&gt;Implicit Modules 的问题&lt;/h2&gt;
&lt;h3 id="原理"&gt;原理&lt;/h3&gt;
&lt;p&gt;在 Xcode 16 之前，Clang Modules 以 &lt;strong&gt;隐式&lt;/strong&gt; 方式工作：&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>编译优化-Swift编译优化</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/swift%E7%BC%96%E8%AF%91%E4%BC%98%E5%8C%96/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/swift%E7%BC%96%E8%AF%91%E4%BC%98%E5%8C%96/</guid><description>&lt;p&gt;Swift 的类型推导、泛型约束求解、WMO / CMO 是编译性能的核心影响因素。理解它们能让源码层面的小改动带来显著的编译加速。本文聚焦&amp;quot;代码写法 + 编译开关&amp;quot;两方面的优化。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="swift-编译流程速览"&gt;Swift 编译流程速览&lt;/h2&gt;
&lt;p&gt;相比 C/OC，Swift 编译多出大量工作：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart LR
A[Parse] --&gt; B[Sema&lt;br/&gt;类型检查]
B --&gt; C[SILGen&lt;br/&gt;生成 SIL]
C --&gt; D[SIL Optimization]
D --&gt; E[IRGen&lt;br/&gt;LLVM IR]
E --&gt; F[LLVM Optimization]
F --&gt; G[CodeGen]
&lt;/pre&gt;
&lt;p&gt;其中 &lt;strong&gt;Sema（语义分析）&lt;/strong&gt; 阶段负责类型推导，是 Swift 特有的性能热点。&lt;code&gt;-debug-time-compilation&lt;/code&gt; 看到的 &amp;ldquo;Type checking&amp;rdquo; 时间基本都集中在这里。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="类型推导与-too-complex-错误"&gt;类型推导与 &amp;ldquo;Too Complex&amp;rdquo; 错误&lt;/h2&gt;
&lt;h3 id="约束求解"&gt;约束求解&lt;/h3&gt;
&lt;p&gt;Swift 的类型系统比大多数语言复杂：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;函数重载 + 运算符重载&lt;/li&gt;
&lt;li&gt;隐式字面量类型（&lt;code&gt;Int&lt;/code&gt;、&lt;code&gt;Double&lt;/code&gt;、&lt;code&gt;Float80&lt;/code&gt;…）&lt;/li&gt;
&lt;li&gt;泛型 + 协议 + 关联类型&lt;/li&gt;
&lt;li&gt;闭包参数类型推导&lt;/li&gt;
&lt;li&gt;SwiftUI 风格的 ViewBuilder / Result Builders&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;遇到表达式 &lt;code&gt;let x = a + b * c - d&lt;/code&gt;，编译器需要给 &lt;code&gt;+ * -&lt;/code&gt; 每个运算符枚举所有可能的重载，在多个候选里做约束传播。当候选组合爆炸时，Sema 会放弃并抛出：&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>编译优化-Xcode构建系统</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/xcode%E6%9E%84%E5%BB%BA%E7%B3%BB%E7%BB%9F/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/xcode%E6%9E%84%E5%BB%BA%E7%B3%BB%E7%BB%9F/</guid><description>&lt;p&gt;Xcode 的构建系统由多个组件协同完成，从 &lt;code&gt;Cmd+B&lt;/code&gt; 到生成 &lt;code&gt;.app&lt;/code&gt; 经历了完整的任务图构建、依赖分析、并行调度流程。理解这套体系是后续一切优化的基础。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="构建系统组成"&gt;构建系统组成&lt;/h2&gt;
&lt;p&gt;Xcode 10 之后默认采用 &lt;strong&gt;Swift Build System&lt;/strong&gt;（基于 llbuild），整体架构如下：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TD
A[Xcode IDE] --&gt; B[XCBBuildService&lt;br/&gt;Daemon]
B --&gt; C[Build Description&lt;br/&gt;任务图 JSON]
C --&gt; D[llbuild&lt;br/&gt;调度内核]
D --&gt; E1[Swift Driver / swiftc]
D --&gt; E2[clang]
D --&gt; E3[ld / ld-prime]
D --&gt; E4[actool / ibtool / 脚本]
E1 &amp; E2 &amp; E3 &amp; E4 --&gt; F[产物缓存]
&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&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;code&gt;XCBBuildService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;独立进程，为 Xcode / xcodebuild 提供构建 RPC 服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Description&lt;/td&gt;
&lt;td&gt;根据 Scheme、Target、Build Settings 生成的任务图&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;llbuild&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;通用构建引擎，执行任务图、做增量与并行调度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;swift-driver&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swift 编译驱动，生成 frontend job 并驱动 swiftc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clang&lt;/code&gt; / &lt;code&gt;swift-frontend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真正的编译器前端&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ld&lt;/code&gt; / &lt;code&gt;ld-prime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;链接器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="xcbbuildservice"&gt;XCBBuildService&lt;/h3&gt;
&lt;p&gt;Xcode 和 &lt;code&gt;xcodebuild&lt;/code&gt; 都是&lt;strong&gt;客户端&lt;/strong&gt;，真正的构建逻辑在 &lt;code&gt;XCBBuildService&lt;/code&gt; 这个独立进程。它通过 XPC 接收请求，生成并执行任务。社区工具 &lt;a href="https://github.com/target/XCBBuildServiceProxy"&gt;&lt;code&gt;XCBBuildServiceProxy&lt;/code&gt;&lt;/a&gt; 利用这一架构在中间插一层代理，实现自定义构建（Bazel、远程执行等）。字节 &lt;code&gt;BitSky&lt;/code&gt;、Tuist Cloud 都基于此类模式。&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>3D球场项目 (六)：客户端工程化与优化</title><link>https://amatsuzero.github.io/LastStand/posts/cocostennis/part-six/</link><pubDate>Sun, 31 May 2026 09:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/cocostennis/part-six/</guid><description>&lt;p&gt;&lt;a href="../part-five/"&gt;3D球场项目 (五)&lt;/a&gt; 把客户端的核心业务流
讲完了：Native 推送 → DataManager 队列 → ScenePlayer 章节调度 → GameController 状态机
→ Handler → 渲染层。功能闭环跑通之后，真正的硬仗是 &lt;strong&gt;&amp;ldquo;工程化&amp;quot;和&amp;quot;性能&amp;rdquo;&lt;/strong&gt;——前者保证
项目能稳定上线（编译跑通、退出不崩、Swift 调用顺手），后者保证在中低端机上画面流畅。&lt;/p&gt;
&lt;p&gt;第一版上线时遇到了两类问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;稳定性 / 工程化&lt;/strong&gt;：modulemap 编译报错、lipo 架构冲突、A11 设备启动 crash、
Cocos 退出期 OpenAL 资源竞争 crash……这些都不是&amp;quot;画面卡&amp;quot;，但每一个都能让用户体验
归零；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能&lt;/strong&gt;：中端 iPhone 在比赛回合渲染期间，GPU 和 CPU 都偶尔抖到 80%+，帧率掉到
30 fps 出头。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本篇按这两类拆成两部分，所有改动都遵循一条原则：&lt;strong&gt;不动玩法，只把工程问题和帧时间
解决掉&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="架构改动一览"&gt;架构改动一览&lt;/h2&gt;
&lt;p&gt;&lt;a href="../part-two/"&gt;3D球场项目 (二)&lt;/a&gt; 介绍过弹幕引擎的四层
架构（腾讯视频 APP / MagicDanmakuiOS / MagicDanmaku / cocos-engine）。这一阶段
&lt;strong&gt;每一层都动了&lt;/strong&gt;——3D 球场不是单点改造，而是一次自上而下的纵贯。下面这张图是把
PartTwo 那张原始架构图重画一遍，&lt;strong&gt;改动的节点用蓝色&lt;/strong&gt;、&lt;strong&gt;新增的节点用绿色&lt;/strong&gt;，
没动的节点保持白色：&lt;/p&gt;
&lt;div class="arch-diagram arch-diff"&gt;
&lt;style&gt;
.arch-diff { font-size: 13px; color: #222; margin: 1.2em 0; max-width: 100%; overflow-x: auto; background: #f0f4f8; border: 1.5px solid #aab4c2; border-radius: 8px; padding: 14px 16px 16px; }
.arch-diff .layer {
background: #f0f4f8;
border: 1.5px solid #aab4c2;
border-radius: 8px;
padding: 14px 16px 16px;
margin: 10px 0;
position: relative;
}
.arch-diff .layer-title {
font-weight: 600;
color: #334155;
margin-bottom: 10px;
font-size: 14px;
}
.arch-diff .row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.arch-diff .node {
background: #fff;
border: 1px solid #8b8b8b;
border-radius: 18px;
padding: 6px 14px;
min-width: 60px;
text-align: center;
box-shadow: 0 1px 1px rgba(0,0,0,0.03);
flex: 1 1 auto;
white-space: nowrap;
}
.arch-diff .node.changed {
background: #dbeafe;
border-color: #2563eb;
color: #1e3a8a;
}
.arch-diff .node.added {
background: #dcfce7;
border-color: #16a34a;
color: #14532d;
font-weight: 600;
}
.arch-diff .node.wrap {
white-space: normal;
line-height: 1.35;
padding: 8px 12px;
}
.arch-diff .group {
background: #fafbfc;
border: 1px dashed #9aa4b2;
border-radius: 8px;
padding: 12px;
flex: 1;
min-width: 200px;
display: flex;
flex-direction: column;
}
.arch-diff .group-title {
font-weight: 600;
color: #475569;
margin-bottom: 8px;
font-size: 13px;
}
.arch-diff .inner {
background: #fff;
border: 1px solid #c5ccd6;
border-radius: 6px;
padding: 10px;
margin-bottom: 8px;
}
.arch-diff .inner:last-child { margin-bottom: 0; }
.arch-diff .inner-title {
color: #475569;
font-size: 12px;
text-align: center;
margin-bottom: 6px;
}
.arch-diff .arrow {
text-align: center;
color: #94a3b8;
font-size: 18px;
line-height: 1;
margin: 4px 0;
user-select: none;
}
.arch-diff .app-row { display: flex; justify-content: center; }
.arch-diff .app-row .node { flex: 0 0 auto; min-width: 120px; padding: 8px 24px; }
.arch-diff .md-row { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
.arch-diff .md-row &gt; .group:first-child { flex: 2.4 1 360px; }
.arch-diff .md-row &gt; .group:last-child { flex: 1 1 200px; }
.arch-diff .ce-row { display: flex; gap: 10px; align-items: flex-start; flex-wrap: wrap; }
.arch-diff .ce-row .nodes { display: flex; gap: 10px; flex: 1 1 280px; flex-wrap: wrap; }
.arch-diff .ce-row .ext-group { flex: 1 1 38%; min-width: 220px; }
.arch-diff .legend {
display: flex;
gap: 14px;
justify-content: center;
align-items: center;
margin-bottom: 10px;
font-size: 12px;
color: #475569;
flex-wrap: wrap;
}
.arch-diff .legend .node {
flex: 0 0 auto;
min-width: 0;
padding: 4px 12px;
font-size: 12px;
}
&lt;pre&gt;&lt;code&gt;@media (max-width: 900px) {
.arch-diff { font-size: 12px; }
.arch-diff .layer { padding: 10px 10px 12px; }
.arch-diff .layer-title { font-size: 13px; margin-bottom: 8px; }
.arch-diff .row { gap: 6px; }
.arch-diff .row &amp;gt; .node {
flex: 1 1 calc(33.333% - 6px);
padding: 5px 6px;
min-width: 0;
font-size: 11.5px;
white-space: normal;
line-height: 1.3;
word-break: break-word;
}
.arch-diff .node.wrap { padding: 6px 8px; line-height: 1.3; }
.arch-diff .group { padding: 8px; min-width: 0; }
.arch-diff .inner { padding: 8px; }
.arch-diff .app-row .node { min-width: 0; padding: 6px 18px; flex: 0 0 auto; white-space: nowrap; }
.arch-diff .md-row { flex-direction: column; gap: 8px; }
.arch-diff .md-row &amp;gt; .group:first-child,
.arch-diff .md-row &amp;gt; .group:last-child { flex: 1 1 auto; width: 100%; }
.arch-diff .ce-row { flex-direction: column; gap: 8px; }
.arch-diff .ce-row .nodes { flex: 1 1 auto; width: 100%; }
.arch-diff .ce-row .ext-group { flex: 1 1 auto; width: 100%; min-width: 0; }
}
@media (max-width: 420px) {
.arch-diff .row &amp;gt; .node { flex: 1 1 calc(50% - 6px); }
}
@media (prefers-color-scheme: dark) {
.arch-diff { color: #e2e8f0; background: #0f172a; border-color: #475569; }
.arch-diff .layer { background: #1e293b; border-color: #475569; }
.arch-diff .group { background: #0f172a; border-color: #475569; }
.arch-diff .inner { background: #1e293b; border-color: #475569; }
.arch-diff .node { background: #0f172a; border-color: #94a3b8; color: #e2e8f0; }
.arch-diff .node.changed { background: #1e3a8a; border-color: #60a5fa; color: #dbeafe; }
.arch-diff .node.added { background: #14532d; border-color: #4ade80; color: #dcfce7; }
.arch-diff .layer-title, .arch-diff .group-title, .arch-diff .inner-title { color: #cbd5e1; }
.arch-diff .legend { color: #cbd5e1; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;</description></item><item><title>编译优化-二进制化</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%8C%96/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%8C%96/</guid><description>&lt;p&gt;二进制化是大型 iOS 工程编译加速的&amp;quot;银弹&amp;quot;：把组件从源码编译改成预编译产物链接，在本地无需再跑 Swift/Clang 前端，直接链接已有的 &lt;code&gt;.a&lt;/code&gt; / &lt;code&gt;.framework&lt;/code&gt; / &lt;code&gt;.xcframework&lt;/code&gt;。美团在 &lt;code&gt;cocoapods-hmap-prebuilt&lt;/code&gt; 之外，还通过二进制化把整体编译速度提升 50%+。本文系统介绍二进制化的原理、实现方式与工具链。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="为什么能加速"&gt;为什么能加速&lt;/h2&gt;
&lt;p&gt;典型项目的编译时间分布（抖音量级）：&lt;/p&gt;
&lt;pre class="mermaid"&gt;pie title 冷编译耗时占比
"业务代码编译" : 25
"第三方依赖编译" : 50
"链接" : 15
"其他" : 10
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;超过一半的时间都花在编译 &amp;ldquo;不会改&amp;rdquo; 的依赖上&lt;/strong&gt;。把依赖预编译成二进制，本地只需要链接，这部分耗时直接归零。再配合远程缓存，第一次拉代码的同学也能命中他人产物。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二进制产物形态"&gt;二进制产物形态&lt;/h2&gt;
&lt;h3 id="静态库a"&gt;静态库（.a）&lt;/h3&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;libSDWebImage.a
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SDWebImage/Headers/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── SDWebImage.h
&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;
&lt;ul&gt;
&lt;li&gt;最小体积，Mach-O 里没有 LC_SEGMENT_64 / LC_LOAD_DYLIB 负担&lt;/li&gt;
&lt;li&gt;启动最快，无 dyld 成本&lt;/li&gt;
&lt;li&gt;静态链接时整合到主可执行&lt;/li&gt;
&lt;li&gt;不支持资源文件（需要单独管理 bundle）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="frameworkframework"&gt;Framework（.framework）&lt;/h3&gt;
&lt;p&gt;Apple 推荐的打包形态：&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SDWebImage.framework/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── SDWebImage # 二进制（静态或动态）
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── Headers/ # 公开头文件
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── Modules/ # modulemap、swiftmodule
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── module.modulemap
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── SDWebImage.swiftmodule/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── arm64-apple-ios.swiftmodule
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── arm64-apple-ios.swiftinterface
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── Info.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过 &lt;code&gt;Mach-O Type&lt;/code&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/interview/ios-build/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%8C%96%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</link><pubDate>Fri, 08 May 2026 22:56:38 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%8C%96%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</guid><description>&lt;p&gt;本文结合 &lt;a href="https://github.com/tripleCC/cocoapods-bin"&gt;cocoapods-bin&lt;/a&gt;（社区最成熟的二进制化插件）拆解&amp;quot;Pod 二进制化&amp;quot;背后的工程机制：集成时如何透明替换 spec、打包时如何还原 Xcode 产物、调试时如何跳回源码。文末给出&amp;quot;自研一套二进制系统&amp;quot;的落地 checklist。&lt;/p&gt;
&lt;p&gt;想先了解二进制化的整体背景、坑点和适用场景，可以先看 &lt;a href="https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%8C%96/"&gt;编译优化-二进制化&lt;/a&gt;；想了解 pod install 阶段本身的优化（HMap、并发下载等）见 &lt;a href="https://amatsuzero.github.io/LastStand/posts/interview/ios-build/cocoapods%E4%BC%98%E5%8C%96/"&gt;编译优化-CocoaPods优化&lt;/a&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="总体架构"&gt;总体架构&lt;/h2&gt;
&lt;p&gt;一套完整的 Pod 二进制化系统由三层组成：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TB
subgraph CI[打包 CI]
A1[源码 podspec] --&gt; A2[壳工程 pod install]
A2 --&gt; A3[xcodebuild 双 SDK 编译]
A3 --&gt; A4[lipo / create-xcframework]
A4 --&gt; A5[上传 OSS]
A5 --&gt; A6[生成 binary podspec]
A6 --&gt; A7[push 到二进制仓]
end
subgraph Dev[开发机 pod install]
B1[Podfile 声明 plugin] --&gt; B2[Resolver/LazySpecification Hook]
B2 --&gt; B3{二进制仓&lt;br/&gt;是否有该版本?}
B3 -- 是 --&gt; B4[替换为 binary spec]
B3 -- 否 --&gt; B5[回退源码 spec]
B4 --&gt; B6[pod download zip]
B5 --&gt; B6
B6 --&gt; B7[Xcode 链接]
end
subgraph Debug[调试]
C1[dwarfdump 读取&lt;br/&gt;DW_AT_comp_dir] --&gt; C2[下载源码]
C2 --&gt; C3[软链到 DWARF 路径]
C3 --&gt; C4[LLDB 自动跳转源码]
end
A7 --&gt; B3
A3 -.DWARF 路径信息.-&gt; C1
&lt;/pre&gt;
&lt;p&gt;关键设计点：&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>编译优化-头文件与HMap</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E5%A4%B4%E6%96%87%E4%BB%B6%E4%B8%8Ehmap/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E5%A4%B4%E6%96%87%E4%BB%B6%E4%B8%8Ehmap/</guid><description>&lt;p&gt;对以 Objective-C 为主或混编的 iOS 大型工程，头文件查找是一个被严重低估的编译开销点。美团的统计显示，400+ Pod 组件的工程会产生近 5 万个头文件，导致海量的 IO 操作和编译参数膨胀。Header Map（HMap）技术能把头文件查找从 O(n) 的目录扫描退化为 O(1) 的哈希查表。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="头文件查找的代价"&gt;头文件查找的代价&lt;/h2&gt;
&lt;h3 id="clang-的查找流程"&gt;Clang 的查找流程&lt;/h3&gt;
&lt;p&gt;当 Clang 遇到 &lt;code&gt;#import &amp;lt;AFNetworking/AFNetworking.h&amp;gt;&lt;/code&gt; 时：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TD
A[遇到 #import] --&gt; B{是否系统头?}
B -- 是 --&gt; C[SYSTEM_HEADER_SEARCH_PATHS]
B -- 否 --&gt; D[USER_HEADER_SEARCH_PATHS]
C --&gt; E[按顺序遍历 HEADER_SEARCH_PATHS]
D --&gt; E
E --&gt; F{路径下有吗?}
F -- 否 --&gt; G[下一个路径]
G --&gt; E
F -- 是 --&gt; H[stat + open]
H --&gt; I[解析头文件]
&lt;/pre&gt;
&lt;p&gt;每一次查找都要对&lt;strong&gt;所有&lt;/strong&gt; &lt;code&gt;HEADER_SEARCH_PATHS&lt;/code&gt; 执行 &lt;code&gt;stat(2)&lt;/code&gt; 系统调用，当路径数量达到数千时，光 &lt;code&gt;stat&lt;/code&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>编译优化-编译缓存</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E7%BC%96%E8%AF%91%E7%BC%93%E5%AD%98/</link><pubDate>Fri, 08 May 2026 13:07:14 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E7%BC%96%E8%AF%91%E7%BC%93%E5%AD%98/</guid><description>&lt;p&gt;&amp;ldquo;已经编译过的东西不再编译一遍&amp;rdquo;——这是编译优化的基础原理。Xcode 的增量编译、CocoaPods 的二进制缓存、Bazel 的 Action Cache 都是不同层次的编译缓存。本文聚焦 &lt;code&gt;ccache&lt;/code&gt;、Clang/Swift module cache、远程缓存等通用方案的原理与 iOS 落地。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="缓存分层"&gt;缓存分层&lt;/h2&gt;
&lt;p&gt;缓存按命中粒度可以分为三个层次：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TD
A[编译缓存] --&gt; B[编译器内部缓存&lt;br/&gt;PCH/PCM/module cache]
A --&gt; C[Action 级缓存&lt;br/&gt;ccache/sccache]
A --&gt; D[产物级缓存&lt;br/&gt;framework/xcframework]
B --&gt; B1[进程内复用模块]
C --&gt; C1[按 .o 粒度缓存]
D --&gt; D1[按 Pod/module 缓存]
&lt;/pre&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;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;frontend 解析结果&lt;/td&gt;
&lt;td&gt;Clang ModuleCache、Swift Module Cache&lt;/td&gt;
&lt;td&gt;高（本地）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action&lt;/td&gt;
&lt;td&gt;源文件 → 目标文件&lt;/td&gt;
&lt;td&gt;ccache、sccache、Bazel&lt;/td&gt;
&lt;td&gt;中（取决于参数稳定性）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;产物&lt;/td&gt;
&lt;td&gt;整个 Pod 或 module&lt;/td&gt;
&lt;td&gt;cocoapods-bin、Rugby&lt;/td&gt;
&lt;td&gt;高（版本号稳定）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="clang-module-cache"&gt;Clang Module Cache&lt;/h2&gt;
&lt;h3 id="原理"&gt;原理&lt;/h3&gt;
&lt;p&gt;Clang 的 &lt;code&gt;@import&lt;/code&gt; / &lt;code&gt;@_exported import&lt;/code&gt; 会把外部模块预编译成 &lt;code&gt;.pcm&lt;/code&gt;，缓存到 &lt;code&gt;ModuleCachePath&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/interview/ios-build/%E8%A7%82%E6%B5%8B/</link><pubDate>Tue, 05 May 2026 00:41:41 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E8%A7%82%E6%B5%8B/</guid><description>&lt;p&gt;&amp;ldquo;无法度量就无法优化&amp;rdquo;。在开始任何编译优化动作之前，必须先建立一套可重复、可对比的观测手段，否则改动的真实收益无从谈起。本文介绍 iOS 编译耗时观测的主要工具链和原理。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="观测目标分层"&gt;观测目标分层&lt;/h2&gt;
&lt;p&gt;不同层次的观测工具回答不同的问题：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TD
A[编译耗时观测] --&gt; B[整体耗时]
A --&gt; C[阶段耗时]
A --&gt; D[任务级耗时]
A --&gt; E[函数/表达式级]
B --&gt; B1[xcodebuild 总耗时]
C --&gt; C1[Build Timing Summary]
C --&gt; C2[Pod Install 阶段计时]
D --&gt; D1[Build Timeline]
D --&gt; D2[XCLogParser]
E --&gt; E1[-debug-time-compilation]
E --&gt; E2[-warn-long-expression]
&lt;/pre&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;code&gt;time xcodebuild&lt;/code&gt;、MetricKit&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;code&gt;-showBuildTimingSummary&lt;/code&gt;、Build Timing Summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;任务&lt;/td&gt;
&lt;td&gt;哪个文件/目标最慢？&lt;/td&gt;
&lt;td&gt;Xcode Build Timeline、XCLogParser&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;code&gt;-debug-time-function-bodies&lt;/code&gt;、&lt;code&gt;-warn-long-expression-type-checking&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="整体耗时"&gt;整体耗时&lt;/h2&gt;
&lt;h3 id="xcodebuild-命令行计时"&gt;xcodebuild 命令行计时&lt;/h3&gt;
&lt;p&gt;最简单也最稳定的方式是直接给 &lt;code&gt;xcodebuild&lt;/code&gt; 加 &lt;code&gt;time&lt;/code&gt;：&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/interview/ios-build/%E9%93%BE%E6%8E%A5%E4%BC%98%E5%8C%96/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E9%93%BE%E6%8E%A5%E4%BC%98%E5%8C%96/</guid><description>&lt;p&gt;链接是 iOS 编译的最后一个阶段，也是大型工程里经常被忽略的瓶颈。Apple 在 WWDC22 的 &amp;ldquo;Link fast: Improve build and launch times&amp;rdquo; 演讲中公开：Xcode 14 新的 &lt;code&gt;ld-prime&lt;/code&gt; 链接器比 &lt;code&gt;ld64&lt;/code&gt; 快 &lt;strong&gt;2 倍&lt;/strong&gt;。随着 Xcode 17、ThinLTO、Mergeable Libraries 等改进，链接优化已经成为编译加速的重要一环。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="链接器的职责"&gt;链接器的职责&lt;/h2&gt;
&lt;p&gt;链接器把多个 &lt;code&gt;.o&lt;/code&gt; 合并成最终可执行文件或库：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TD
A[若干 .o] --&gt; L[链接器]
B[静态库 .a] --&gt; L
C[动态库 .dylib / framework] --&gt; L
L --&gt; D[符号解析]
D --&gt; E[dead_strip]
E --&gt; F[ObjC runtime fix-up]
F --&gt; G[生成 LC_* 加载命令]
G --&gt; H[写入 Mach-O]
&lt;/pre&gt;
&lt;p&gt;关键任务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;符号解析&lt;/strong&gt;：所有 undefined symbol 必须在某个 .o / .a / .dylib 里找到&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重定位&lt;/strong&gt;：把符号引用的偏移写入正确位置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dead Strip&lt;/strong&gt;：去掉未被使用的代码/数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ObjC 元数据修复&lt;/strong&gt;：把分散的类、分类信息合并成 ObjC runtime 能识别的结构&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生成 Mach-O&lt;/strong&gt;：写 Load Commands、&lt;code&gt;__LINKEDIT&lt;/code&gt; 等段&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="链接器对比"&gt;链接器对比&lt;/h2&gt;
&lt;h3 id="ld64经典"&gt;ld64（经典）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ld64&lt;/code&gt; 是 Apple 长期使用的经典链接器，源代码在 &lt;a href="https://github.com/apple-oss-distributions/ld64"&gt;apple-oss-distributions/ld64&lt;/a&gt;。单线程为主，在大工程上明显偏慢。&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/interview/ios-build/%E7%BC%96%E8%AF%91%E4%BC%98%E5%8C%96/</link><pubDate>Wed, 27 May 2026 22:24:03 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ios-build/%E7%BC%96%E8%AF%91%E4%BC%98%E5%8C%96/</guid><description>&lt;p&gt;大型 iOS 工程的编译耗时往往是研发效能最突出的瓶颈之一。以抖音、今日头条、美团为代表的一线团队，工程代码量通常在数百万到千万行级，本地全量编译耗时 10 分钟以上、CI 上半小时起步已是常态。编译等待不仅直接压缩研发时间，还会打断心流、降低人均产出。&lt;/p&gt;
&lt;p&gt;本系列文章从 iOS 编译系统的原理出发，结合社区最新实践（Xcode 16/17 Explicit Modules、rules_xcodeproj、seer-optimize、cocoapods-hmap-prebuilt、Rugby 等），系统性介绍编译优化的各类手段与背后的原理。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="编译流程概述"&gt;编译流程概述&lt;/h2&gt;
&lt;p&gt;理解&lt;a href="https://amatsuzero.github.io/LastStand/posts/interview/ios-basics/ios%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86/"&gt;iOS编译原理&lt;/a&gt;是编译优化的前提。一次典型的 iOS 编译流程可以划分为以下阶段：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TD
A[Parse Podfile / 生成工程] --&gt; B[Pod Install / 依赖解析]
B --&gt; C[Xcode Build System 调度]
C --&gt; D[Dependency Scan / 模块依赖扫描]
D --&gt; E[Swift/Clang 前端编译]
E --&gt; F[生成 .o / .swiftmodule / .pcm]
F --&gt; G[链接 ld64 / ld-prime / lld]
G --&gt; H[签名 / 打包 / 资源处理]
H --&gt; I[产出 .app / .ipa]
&lt;/pre&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;Pod Install、SPM 解析&lt;/td&gt;
&lt;td&gt;Source 更新慢、Specification 解析重复、沙盒拷贝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;工程生成&lt;/td&gt;
&lt;td&gt;生成 Pods.xcodeproj、xcconfig、hmap&lt;/td&gt;
&lt;td&gt;Target 数量多、pbxproj 膨胀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;构建调度&lt;/td&gt;
&lt;td&gt;Build System 解析任务图、并行调度&lt;/td&gt;
&lt;td&gt;依赖粒度过粗、并行度不足&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模块扫描&lt;/td&gt;
&lt;td&gt;Clang/Swift 依赖扫描&lt;/td&gt;
&lt;td&gt;隐式模块重复编译&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;源码编译&lt;/td&gt;
&lt;td&gt;Swift/Clang 前端、类型检查、SIL/IR 生成&lt;/td&gt;
&lt;td&gt;类型推导爆炸、WMO 关闭、PCH 失效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;链接&lt;/td&gt;
&lt;td&gt;符号解析、LTO、dead-strip&lt;/td&gt;
&lt;td&gt;链接参数过长、ThinLTO 串行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;收尾&lt;/td&gt;
&lt;td&gt;签名、资源拷贝、dSYM&lt;/td&gt;
&lt;td&gt;串行脚本阻塞&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="优化思路全景"&gt;优化思路全景&lt;/h2&gt;
&lt;p&gt;编译优化的核心思路只有三条：&lt;strong&gt;减少需要做的工作&lt;/strong&gt;、&lt;strong&gt;让必须做的工作更快&lt;/strong&gt;、&lt;strong&gt;让做过的工作可复用&lt;/strong&gt;。所有社区实践都可以归入这三类。&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><item><title>工程化 面试题</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/interview-questions/tag-19-539fb4b4/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/interview-questions/tag-19-539fb4b4/</guid><description>&lt;p&gt;共 87 道 工程化 面试题。答案默认折叠，便于先自行作答。&lt;/p&gt;
&lt;h2 id="question-7d4e4120-8c99-4e2e-a2c7-98d186c57289"&gt;1. webpack 中的 Loader ，链式调用顺序会影响编译结果吗？&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;难度：2 · 类型：QA&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="题目要点"&gt;题目要点&lt;/h3&gt;
&lt;p&gt;Webpack Loader 是一个按流水线执行的源码转换系统，Loader 的链式顺序直接决定源码被处理的阶段；配置顺序为从左到右，但执行顺序为从右到左，每个 Loader 都处理前一个 Loader 的输出，因此顺序错误会导致语义阶段错乱甚至编译失败；通常应遵循“靠近源码的 Loader 放右侧，靠近运行时的 Loader 放左侧”的原则，同时 Loader 还存在 pitch 与 normal 两阶段执行机制，使顺序对最终编译结果具有决定性影响。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;参考答案&lt;/summary&gt;
&lt;p&gt;会，而且这是 Webpack Loader 机制中&lt;strong&gt;非常关键且经常被误解的一点&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Loader 的链式调用顺序不仅会影响编译结果，而且很多语法是否能正确工作，完全取决于 Loader 的执行顺序。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;理解这个问题的关键在于：&lt;strong&gt;Loader 本质是一个“源码转换流水线（transform pipeline）”。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一loader-本质源码的逐步转换"&gt;一、Loader 本质：源码的逐步转换&lt;/h2&gt;
&lt;p&gt;在 Webpack 中 hookup 的 Loader，并不是同时运行的，它们会对模块源码进行&lt;strong&gt;一层一层的转换&lt;/strong&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;原始源码 → loaderA → loaderB → loaderC → 最终 JS 模块
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个 Loader：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接收上一个 Loader 的输出&lt;/li&gt;
&lt;li&gt;返回新的源码字符串（或 AST 转换结果）&lt;/li&gt;
&lt;li&gt;交给下一个 Loader&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此：&lt;/p&gt;</description></item></channel></rss>