共计 3806 个字符,预计需要花费 10 分钟才能阅读完成。
为什么要「按需引入」
通常状况下,按需引入区别于异步加载,然而本文会对立讲述这些「有须要时」才去拿取或剔除相干资源的相似场景,因而题目中的「按需引入」代表了这些做法的泛概念。
随着我的项目体量晋升,如果咱们不作干预,可能会碰到我的项目首页加载慢、chunk 体积大、各环境配置未互相拆散、调试代码需手动剔除等一系列问题,其中有不少问题是构建行为不合理引起的。本文就这些问题分享一些「按需引入」的实现形式。
异步加载之 import()
import()
语法分为了动态引入以及动静引入。
动态引入无需多说,动静引入也已在 ES6 中失去了实现,import
在反对 ES6 语法的环境中将会是一个全局函数,它接管须要动静引入的脚本地址,返回一个 Promise
,其异步返回值将会是一个对象,对象中蕴含动静引入模块的导出信息。
为了兼容性,Webpack 将会对 import()
语法作编译解决,将其转换为 WebpackJsonp 的加载形式。Webpack 将引入的模块独自打包为一个 chunk,在须要用到这部分代码时再应用注入 <script>
标签的形式将其申请到本地并运行。此外,Webpack 还对这个 API 进行了一些能力扩大,比方能够在其中书写 /* webpackChunkName: "xxx" */
这样的正文实现自定义这个模块的 chunk 名称。
ES6 的原生应用形式
Webpack 的编译后果
按需引入之 babel-plugin-import
babel-plugin-import
是一个解决模块导入的 Babel 插件 。
它对三方库进行按需引入的解决在默认配置下有些约定俗成的象征,它要求三方库中领有 lib
文件夹,并且在 lib
文件夹中领有和三方库同名(camel-case)的模块文件。抛开默认状况,它也提供了一系列配置让咱们能够自定义三方库的导入模式。这个个性能够实现诸如依据环境抉择导入不同的模块、组件库的按需引入、工具函数库的按需引入等一系列需要。
以默认配置为例按需引入 Ant Design 组件库
配置文件 .babelrc
业务代码
编译后果
咱们能够分明地看到,原来间接从 antd
引入的 Button
组件被编译成了从 antd/lib/button
引入。
以自定义配置为例按需引入 Ant Design 组件库
配置文件 babel.config.js
咱们也可能通过自定义 customName
函数来实现变更引入门路的成果,应用办法和其余配置能够自行查阅 Babel 官网。
插个题外话,咱们也可能本人编写一个 Babel 插件来实现针对外部库的按需导入,思路是通过拦挡自定义 Babel 插件 visitor
配置中的 ImportDeclaration
节点,依据节点配置决定应用哪个其余模块门路进行替换。
以自定义 Babel 插件为例按需引入
这是咱们团队制作的自定义 Babel 插件的一部分,它实现了相似于将 import {abc} from 'xxx';
在编译时转换为 import abc from 'xxx/lib/abc.js
这样的成果,和 babel-plugin-import
实现的成果十分相似。
按需剔除之「Tree Shaking」
人肯定要做好减法,一直专一聚焦,零碎也是如此。绝对于按需引入,Tree Shaking 从反方向聚焦于按需剔除。
Tree Shaking 是一个术语,通常用于形容移除 JavaScript 上下文中的未援用代码(dead-code)。它依赖于 ES2015 模块语法的「动态构造」个性,例如
import
和export
。这个术语和概念实际上是由 ES2015 模块打包工具 Rollup 遍及起来的。
什么是未援用的代码
说到「未援用」,这里要先明确「污浊」和「副作用」这两个概念,咱们先看上面两段代码。
污浊(pure)的代码
有副作用(side-effect)的代码
「污浊」在文件的维度指的是 纯正的 ES2015 模块 ,这种模块只蕴含导出代码,不蕴含其余有「副作用」的代码,从下面的代码样例咱们就能够很容易了解,所谓「副作用」指的就是在模块被导入时会有其余影响以后环境代码被执行。
Tree Shaking 的用处其实就是剖析代码,将未导入的并且没有副作用的模块(文件或部分代码)在最终构建后果中删除。
如何剔除无副作用的代码
Webpack 4 人造反对了 Tree Shaking,咱们必须执行以下操作确保其失效:
- 应用 ES2015 模块语法(即
import
和export
); - 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS(顺带一提,这是当初罕用的 @babel/preset-env 的默认行为,详细信息请参阅 文档);
- 在我的项目的
package.json
文件中,增加sideEffects
属性来标记哪些文件是有副作用的; - 如有须要也能够在模块中标记部分有副作用的代码为污浊的;
- 应用
mode
为production
的配置项以启用 更多优化项,包含压缩代码与 Tree Shaking。
回到这个术语「Tree Shaking」的含意上,字面意思是摇摆树。
你能够将应用程序设想成一棵树。绿色示意理论用到的源码和库,是树上活的树叶。灰色示意未援用代码,是秋天树上枯败的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。
按需引入之「条件编译」
接触过 uni-app 的同学对条件编译应该不生疏,上面是应用了 uni-app 的条件编译示例:
demo.html
demo.js
下面的代码示例意思是,当平台为微信或百度小程序时才会将上述 HTML 代码编译进这两个平台的小程序最终代码中,以及当平台为 APP 时才将上述 JavaScript 代码编译进最终代码中。
这就是条件编译的概念,它间接在编译阶段就进行逻辑辨别,避免无关代码净化各个输入后果。通过条件编译,咱们就能实现一份代码依据不同环境或输入指标来体现不同的展现模式,而不用思考运行时判断代码的节约以及相干性能问题。
通过 Webpack 的三方插件,咱们能很轻松地实现条件编译这种成果,本文介绍两款三方插件——传统的 uglify-js-plugin 和 js-conditional-compile-loader,上面就简略讲述一下它们的应用办法。
uglify-js-plugin
咱们对 webpack.config.js
进行配置:
之后在业务代码中咱们间接这么写:
下面的代码意思就是当 ENV_TEST
值为 true
时,才将 if
中的代码编译进后果中。
应用 uglify-js-plugin
进行条件编译的形式相当于是曲线救国,它自身不具备条件编译的性能,然而它有一个剔除死代码的性能,死代码的意思如同:
咱们很容易看出这段代码永远不会被执行,这就称为死代码。
咱们能够通过应用 define-plugin
插件来使一段代码成为死代码,办法就是 if
中的条件写成咱们通过 define-plugin
定义的全局变量,而这个变量在某些环境下会是永远的 false
,这就成了死代码。所以说应用 uglify-js-plugin
和 define-plugin
进行条件编译的形式是曲线救国。
js-conditional-compile-loader
js-conditional-compile-loader
是一个业余做条件编译的 Webpack Loader,相比于 uglify-js-plugin
曲线救国的形式,它更灵便,实践上它反对任何文件中任何局部的条件编译,相当于它是将文件当成了字符串进行解决,在某些环境下保留或剔除被特定格局包裹的字符串。
比方咱们在一个 Vue 组件中应用它:
它决定是否剔除 正文包裹内容 的形式也是通过它的 Loader 配置实现的,相当于应用了它外部的环境变量,上面咱们就来看一下它是如何被配置到 Webpack 中的。
这里须要留神,js-conditional-compile-loader
须要作为解决源文件的第一步,即放在每种文件解决规定中 loader
数组的开端。起因很容易想,因为防止其余的 Loader 扭转文件构造,导致文件内容发生变化而不失效。
小结
异步加载
import()
- ES6
- WebpackJsonp
按需引入
babel-plugin-import
customName
ImportDeclaration
按需剔除 Tree Shaking
- 污浊(pure)
- 副作用(side-effect)
条件编译
uglify-js-plugin
js-conditional-compile-loader
咱们总共学习了四大类「按需引入」的形式,置信这些形式能解决咱们工作中不少构建问题。我在日常工作中秉承的准则是能不反复造轮子就不反复造轮子,有优良的三方库就间接应用,当然如果咱们有本人的个性化需要,也能够参考他们这些优良的实现形式来发明咱们本人的提效工具。
参考:
- 《你的 import 被 webpack 编译成了什么?》:https://juejin.cn/post/685956…
- 《NPM babel-plugin-import》:https://www.npmjs.com/package…
- 《Tree Shaking》:https://webpack.docschina.org…
- 《条件编译》:https://blog.csdn.net/qiuziji…
- 《js-conditional-compile-loader》:https://github.com/hzsrc/js-c…