接上一篇文章:閱讀《Vue.js設計與實現》 – 01
文章目錄
- 第二章
- 提升用戶的開發體驗
- tips
- 控制框架代碼的體積
- Tree-Shaking
- 副作用
- 框架應該輸出怎樣的構建產物?
- 注意
- 這兩個文件有什么區別?
- 特性開關
- 如何實現?
- 處理錯誤
- TS支持
第二章
本書第二章,主要是告訴我們一個好的框架需要考慮的事情很多,包括:構建的產物是什么?產物構建模式?提升用戶的開發體驗?熱更新?減少打包體積?可配置?……
這部分主要就是說:vue怎么樣做到這些事情,以及vue怎么好的。屬于自賣自夸,手動狗頭!
提升用戶的開發體驗
衡量一個框架是否足夠優秀的指標之一,就是看它的開發體驗如何。
說白了就是出現問題提示我們程序員夠不夠清楚,別出個問題全是源碼提示,一點進去直接跳進源碼(說實話,菜鳥開發vue經常性這樣,所以感覺有點自賣自夸),《Vue.js設計與實現》 中舉例的是 createApp
掛載元素不存在時的一個 wran
,那確實提示得很清楚
這要是都提示不清楚,那也沒必要說 vue 好用了,畢竟掛載上去是最重要的一點!
如果 Vue.js 內部不做任何處理,那么我們很可能得到的是 JavaScript 層面的錯誤信息,例如 Uncaught TypeError: Cannot read property 'xxx' of null
,根據此信息,我們很難知道問題出在哪里。
tips
我們在寫代碼打印 ref
對象時,如果不加 .value
就打印,打印出來的結構還是比較難看的,打印結果需要我們手動點擊進去,且有很多我們不關注的無用信息。
但是其實瀏覽器允許我們編寫自定義的 formatter,從而自定義輸出形式。在 Vue.js3
的源碼中,你可以搜索到名為 initCustomFormatter
的函數,該函數就是用來在開發環境下初始化自定義 formatter 的。
我們只需要設置一下goole控制臺,即可優化打印結果,步驟如下:
1、打開控制臺,點擊如圖按鈕
2、找到控制臺,開啟 Enablecustom formatters
即可
設置之后的打印結果會變成
控制框架代碼的體積
這部分主要講的就是:框架在實現相同功能時,肯定是代碼越少越好,這樣體積更小,請求后加載速度就更快!但是這個和上一個違背了,因為要處理好報錯,肯定就要加一堆不相關的邏輯。vue想辦法解決了這個問題,就是區分環境,用rollup
里面的插件來設置一個Boolean
值(源碼為__DEV__
常量 ),讓開發環境有提示,生產環境因為rollup
的Tree-Shaking
,就不會打包進入生產環境,減小體積!
Tree-Shaking
什么是 Tree-Shaking
?在前端領域,這個概念因 rollup.js
而普及,指的就是消除那些永遠不會被執行的代碼,現在無論是 rollup.js
還是 webpack
,都支持 Tree-Shaking
。
想要實現 Tree-Shaking
,必須滿足一個條件,即模塊必須是ESM(ES Module)
,因為 Tree-Shaking
依賴 ESM
的靜態結構。
這里《Vue.js設計與實現》舉了一個rollup
打包的例子,一個input.js
文件
import { foo } from './utils.js'
一個utils.js
文件
export function foo(obj) {obj && obj.foo
}export function bar(obj) {obj && obj.bar
}
使用 rollup 打包后,打包文件只會留下 foo 函數,bar 函數會被Tree-Shaking
掉!
但是又發現其實 foo 也沒有使用,只是引用了而已,按理說應該也要將其刪除!
副作用
當調用函數的時候會對外部產生影響。這時你可能會說,上面的代碼明顯是讀取對象的值,怎么會產生副作用呢?其實是有可能的,試想一下,如果 obj 對象是一個通過 Proxy 創建的代理對象,那么當我們讀取對象屬性時,就會觸發代理對象的 get ,在 get 中是可能產生副作用的,例如:我們在 get 夾子中修改了某個全局變量。而到底會不會產生副作用,只有代碼真正運行的時候才能知道,JavaScript 本身是動態語言,因此想要靜態地分析哪些代碼是永遠不會被使用的很有難度。
因為靜態地分析 JavaScript 代碼很困難,所以像 rollup.js 這類工具都會提供一個機制,讓我們能明確地告訴 rollup.js:“放心吧,這段代碼不會產生副作用,你可以移除它。”
如何告訴rollup呢?使用/*#__PURE__*/
代碼變成:
import {foo} from './utils'
/*#__PURE__*/ foo()
框架應該輸出怎樣的構建產物?
這里講解了rollup
如何打包產物為你需要的格式,如果用戶想直接通過script
直接引入那么就要設置
菜鳥這里直接搞成表格
需要的打包產物 | rollup設置(output/format) |
---|---|
script直接引入 | iife |
script使用module形式引入 | esm |
node中使用require引入 | cjs |
注意
這里第二種其實會產生兩種文件,一種vue.esm-browser.js
,該文件是直接給 <script type="module">
使用的,還有一種vue.esm-bundler.js
,這個文件是開發時,給構建工具(vite 或者 webpack)使用的!
我們知道,無論是 rollup.js 還是 webpack,在尋找資源時,如果 package.json 中存在 module 字段,那么會優先使用 module 字段指向的資源來代替 main 字段指向的資源。
這兩個文件有什么區別?
區別在于上文中的 __DEV__
常量。當構建用于<script>
標簽的 ESM 資源時,如果是用于開發環境,那么__DEV__
會設置為 true;如果是用于生產環境,那么 __DEV__
常量會設置為 false,從而被 Tree-Shaking
移除。
但是當我們構建提供給打包工具的 ESM 格式的資源時,不能直接把 __DEV__
設置為 true或 false,而要使用 (process.env.NODE_ENV !=='production')
替換 __DEV__
常量。
特性開關
就是預留一些配置,用戶通過true或者false(或者其他代碼)來確定自己要不要那些功能,如果用戶想要就在打包時留下那些功能代碼,不想要就直接刪除!
如何實現?
其實很簡單,原理和上文提到的__DEV__
常量一樣,本質上是利用 rollup.js 的預定義常量插件
來實現。
更具體的就想要讀者去看書了,這里不好總結,感覺就是一整段,但是都拿過來就要被寄律師函了,手動狗頭!
死道友不死貧道,手動狗頭,大家可以看這個:https://juejin.cn/post/7091263773301800991?from=search-suggest#heading-17
處理錯誤
框架需要為用戶提供統一的錯誤處理接口,這樣用戶可以通過注冊自定義的錯誤處理函數來處理全部的框架異常。
說實話,菜鳥沒有用過,所以不太清楚這個統一的錯誤處理接口是啥,然后菜鳥問了一下GPT,感覺這里統一的錯誤接口應該就是和js里面的頂層監聽錯誤一個意思!類似:
GPT回答:
菜鳥感覺基本用處不是很大,讀者想了解可以直接vue3官網搜:onErrorCaptured()
--> vue3的一個生命周期!
TS支持
一個常見的認知誤區,即"使用 TS 編寫框架和框架對 TS 類型支持友好是兩件完全不同的事"。有時候為了讓框架提供更加友好的類型支持,甚至要花費比實現框架功能本身更多的時間和精力。
菜鳥只是略懂ts,但是感覺ts讓我們的心智負擔更重而不是減少了!我們程序需要的是:檢驗變量類型,以確保邏輯正確,而不是在項目中一引入就一大堆報錯,然后去找用什么類型才能讓這個組件的類型是對的,以及一些不知道名字的庫的類型是對的,感覺ts檢測的重心應該在邏輯而不是檢驗為什么組件類型不對。
就像這本書說的:
有時候為了讓框架提供更加友好的類型支持,甚至要花費比實現框架功能本身更多的時間和精力。