💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[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(调用)。