[TOC]
# 理解 Tree-Shaking
在構建大型應用時是非常好的,因為日常開發經常需要引用各種庫。但大多時候僅僅使用了這些庫的某些部分,并非需要全部,此時 **Tree-Shaking** 能幫助我們刪除掉沒有使用的代碼,大大縮減打包后的代碼量。
Tree-Shaking 在前端界由 [rollup](https://github.com/rollup/rollup#tree-shaking) 首先提出并實現,后續 [webpack](https://github.com/webpack/webpack) 在 2.x 版本也借助于 [UglifyJS](https://github.com/mishoo/UglifyJS2) 實現了。自那以后,在各類討論優化打包的文章中,都能看到 Tree-Shaking 的身影。
Tree-Shaking 的想法是將代碼的各種依賴關系想象成一棵樹。 一方面,隨著越來越多的依賴關系以附加包的形式添加,該樹隨著時間的推移變得更大,更復雜并且分支更多。 另一方面,較早的依賴關系逐漸不再被集成或使用。 這些包仍然存在,但不再“捆綁”。
當您“搖晃”這棵樹時,沒有牢牢附著在樹干或樹枝上的東西都會掉到地上 - 不再牢固綁緊并因此不再需要的包裹會掉落。
除了最小化和壓縮 JavaScript 代碼外,搖樹可能是加快 Web 應用程序加載時間的最重要工具 - 尤其是因為它直接影響代碼量。
## ESM VS CJS ??
ES 模塊使得 bundlers(例如:webpack / rollup) 可以對你的代碼進行 tree-shaking。如果我沒有記錯的話,這是因為 ESM(ES modules)語法允許 bundlers 靜態地檢查代碼的哪些部分被使用了,哪些沒有,這對于 CJS 來說比較困難,因為它可以以動態的方式使用,比如下面這樣:
```
var my_lib;
if (Math.random()) {
my_lib = require('foo');
} else {
my_lib = require('bar');
}
if (Math.random()) {
exports.baz = "??";
}
```
所以對于一些 UI 組件庫,如果想做到 tree-shaking,就必須使用 ESM。還有其他的方法。Material UI 和 Ant Design 走向不同的方向。它們沒有創建一個可以導出所有組件的包,而是創建了無數個小包,對應每個組件:
~~~
import Button from '@material-ui/core/Button';
// 而不是 import { Button } from '@material-ui';
~~~
上面代碼確實可行,但需要特定的打包設置,而且會存在很大的風險,你的每個組件會一遍又一遍地捆綁重復的 UI 組件代碼。
如果你使用過 MaterialUI / Ant Design,其實我們是可以這么做的:
```
import { DatePicker, message } from 'antd';
```
其實 [Antd](https://ant.design/docs/react/getting-started-cn#%E6%8C%89%E9%9C%80%E5%8A%A0%E8%BD%BD) 會要求您安裝 [babel-plugin-import](https://www.npmjs.com/package/babel-plugin-import),如果使用帶有create-react-app 的 [bonkers 設置](https://ant.design/docs/react/use-with-create-react-app-cn#Advanced-Guides),需要您重新配置 react-scripts。 這個 babel 插件的作用是自動轉換代碼,如下:
```
import { Button } from 'antd';
ReactDOM.render(<Button>xxxx</Button>);
↓ ↓ ↓ ↓ ↓ ↓
var _button = require('antd/lib/button');
ReactDOM.render(<_button>xxxx</_button>);
```
類似 Antd UI庫一般使用 rollup 打包:
```
...
{
output: [
{
file: pkg.main,
format: "cjs",
},
{
file: pkg.module,
format: "es",
},
],
}
...
```
`pkg` 代表了`package.json` 文件內容。一般會打包出 CJS 和 ESM 兩種類型捆綁文件。
為什么需要 CJS 呢?當您使用庫時,如果`package.json`中沒有有效的`main`字段(它指定的文件必須為 CJS 格式),則 Node.js 和其他打包器將無法識別。 另外,這為您提供向后兼容性,但沒有 tree-shaking 功能。
對于生成 ES 模塊,我們從`package.json`的`module`字段中獲取文件名。像 Webpack 和 Rollup 之類的打包工具將識別該字段,并在正確設置后使用它,并期望在它后面有一個 ES 模塊(而忽略`main`字段)。
## 應用 tree-shaking
* 使用 webpack
* 使用 ES2015 模塊語法`import、export`
* 確保諸如 Babel 或預設環境之類的工具不會將您的 ESM 代碼轉換為 CommonJS
```js
...
use: {
loader: babel-loader,
/* 這個配置禁止 babel-preset-env 將導入或導出模塊轉到 CommonJS */
options: {
presets: [
[ 'es2015', { modules: false }]
]
}
}
...
```
* 通過 Webpack 的 Production Build 使用 Uglify 或 Terser 之類的工具來執行 tree-shaking
> [你的 Tree-Shaking 并沒什么卵用](https://segmentfault.com/a/1190000012794598)
> 這邊文章中提到的缺陷,webpack 4 就已經消除了
> https://webpack.js.org/guides/tree-shaking/
# `runtimeChunk`
`runtimeChunk` 分離的是運行時的模塊,它和業務無關,主要包含 **webpack 的一些定義**以及從主模塊分離出去**模塊的信息**。它的代碼量一般比較小,且比較容易變化。
> [webpack#optimizationruntimechunk](https://webpack.docschina.org/configuration/optimization/#optimizationruntimechunk)
> [webpack4中創建內聯runtimeChunk](http://qiutianaimeili.com/html/page/2020/02/koedwmlsoy.html)
# 隨筆分類 - webpack
https://www.cnblogs.com/tugenhua0707/category/1281878.html
# 熱替換
模塊熱替換(Hot Module Replacement)
[https://blog.csdn.net/hbiao68/article/details/104115981](https://blog.csdn.net/hbiao68/article/details/104115981)
[http://www.ayqy.net/blog/hot-module-replacement/](http://www.ayqy.net/blog/hot-module-replacement/)
## HMR 原理
頁面實時刷新已經不再是前端開發過程所追求的功能,熱替換已經是新的潮流,當你修改代碼時,你所改動的地方會實時反映到頁面中,而這個過程并沒有刷新頁面。這里,需要注意的一點,webpack 對于熱替換的機制是不同的處理方式的,在有些情況下是會通過刷新頁面來實現熱加載。當然也可以通過添加參數 `--inline` 來實現熱替換。`webpack-dev-server --hot --inline`
## 參考
* [模塊熱更新指南](https://www.webpackjs.com/guides/hot-module-replacement)
* [概念 - 模塊熱替換](https://www.webpackjs.com/concepts/hot-module-replacement)
* [API - 模塊熱替換](https://www.webpackjs.com/api/hot-module-replacement)
* [Plugins - HotModuleReplacementPlugin](https://www.webpackjs.com/plugins/hot-module-replacement-plugin/)
## [react-hot-loader 和 webpack-dev-server有什么不同](https://www.jianshu.com/p/e22f2286744d)
# 架構
# 核心 Tapable
webpack整體是一個插件架構,所有的功能都以插件的方式集成在構建流程中,通過發布訂閱事件來觸發各個插件執行。webpack核心使用 Tapable 來實現插件(plugins)的 binding(注冊)和 applying(調用)。
- 講解 Markdown
- 示例
- SVN
- Git筆記
- github 相關
- DESIGNER'S GUIDE TO DPI
- JS 模塊化
- CommonJS、AMD、CMD、UMD、ES6
- AMD
- RequrieJS
- r.js
- 模塊化打包
- 學習Chrome DevTools
- chrome://inspect
- Chrome DevTools 之 Elements
- Chrome DevTools 之 Console
- Chrome DevTools 之 Sources
- Chrome DevTools 之 Network
- Chrome DevTools 之 Memory
- Chrome DevTools 之 Performance
- Chrome DevTools 之 Resources
- Chrome DevTools 之 Security
- Chrome DevTools 之 Audits
- 技巧
- Node.js
- 基礎知識
- package.json 詳解
- corepack
- npm
- yarn
- pnpm
- yalc
- 庫處理
- Babel
- 相關庫
- 轉譯基礎
- 插件
- AST
- Rollup
- 基礎
- 插件
- Webpack
- 詳解配置
- 實現 loader
- webpack 進階
- plugin 用法
- 輔助工具
- 解答疑惑
- 開發工具集合
- 花樣百出的打包工具
- 紛雜的構建系統
- monorepo
- 前端工作流
- 爬蟲
- 測試篇
- 綜合
- Jest
- playwright
- Puppeteer
- cypress
- webdriverIO
- TestCafe
- 其他
- 工程開發
- gulp篇
- Building With Gulp
- Sass篇
- PostCSS篇
- combo服務
- 編碼規范檢查
- 前端優化
- 優化策略
- 高性能HTML5
- 瀏覽器端性能
- 前后端分離篇
- 分離部署
- API 文檔框架
- 項目開發環境
- 基于 JWT 的 Token 認證
- 扯皮時間
- 持續集成及后續服務
- 靜態服務器搭建
- mock與調試
- browserslist
- Project Starter
- Docker
- 文檔網站生成
- ddd