[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