按需引入组件库的几种方式

之前升级项目的ui版本时,ui组件的版本错误。无论修改npm版本还是cdn版本均无法解决。
经过同事 @东哥 的排查,发现是样式被覆盖,去除项目中的某sdk组件可恢复正常。
细究原因,项目和sdk中引入了相同ui组件,sdk是使用webpack打包的文件,打包成一个总文件,项目中ui组件版本被覆盖,导致项目样式错误。
// 后续发现应该是 没有把ui组件在打包时externals掉,引入了css文件

同时后续决定了解关于项目实现按需引入的方式。

手动引入

在没有其他辅助手段的情况下,实现按需引入,仅引入需要使用的组件即可。

1
2
3
// test/index.js
import Button from 'test-project/lib/components/Button'
import 'test-project/style/button.css'

结论

手动引入的方式, 需要深入了解组件库代码,了解各组件位置等信息,不方便。

因此,现在大部分组件库已经放弃了这种方式。

Babel-plugin-import插件

让程序帮我们把引入组件的语句转化成手动引入的语句

1
2
// test/index.js
import {Button} from 'test-project'

配置.babelrc文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// test/.babelrc
{
"presets": [
"@babel/preset-env"
],
// 注意:借助babel-plugin-import引入
"plugins": [["import",{
// 组件库名称
"libraryName": "test-project",
// 组件位置
"libraryDirectory": "lib/components",
// "styleLibraryDirectory": "lib/theme", 配置的话会在project/lib/theme/button中引入样式
// 组件样式位置
"style": "style/index.css"
// style:true的时候加载未编译为css的less,也就可以改变主题
// style:“css”就是加载编译好的css,无法更改主题
}]]
}

结论

Babel-plugin-import借助Babel解析编辑ATS的能力实现了一种成熟的组件按需引入方式。

优点:只需要按照babel文档进行配置即可

缺点:代码中需增加本无需的Babel及相关的配置

Tree-Shaking

Tree Shaking中文含义是摇树,在webpack中作用是消除不可能执行的代码。

webpack5已经自带了这个功能了,当打包环境为production时,默认开启tree-shaking功能。

Tree-shaking是依赖ESM这种模块引入方式的:

ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,从而构成了tree-shaking的基础。

所谓静态分析就是不执行代码,从字面量上对代码进行分析,可以想一下在commonjs的模块化中我们可以动态require一个模块,只有执行后才知道引用的什么模块,这个就不能通过静态分析去做优化。总之就是一句话——代码是否有用只有在编译时分析,才不会影响运行时的状态。

在webpack中使用tree-shaking,webpack官方文档的总结如下:

我们学到为了利用 tree shaking 的优势, 你必须…

1、使用 ES2015 模块语法(即 import 和 export)。
2、确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
3、在项目的 package.json 文件中,添加 “sideEffects” 属性。
4、使用 mode 为 “production” 的配置项以启用更多优化项,包括压缩代码与 tree shaking。

webpack5中,mode为production时,已自动开启DCE工具(消除无用代码)UglifyJSPlugin和tree-shaking

组件库打包:支持按需加载

根据以上我们得知,组件库支持按需加载需满足:

  1. 组件库以es6模块化方式导出(注意babel)
  2. package.json中添加sideEffects属性
  3. 宿主项目中引用方式写法到位或者配置了babel-plugin-import
  4. 宿主项目通过webpack打包支持tree-shaking

实现按需加载存在的问题

webpack不支持ESM导出

webpack umd 形式导出,打包出来的东西才能供别人使用

1
2
3
4
5
// webapack.config
output: {
library: 'xmh',
libraryTarget: 'umd'
}

但是是整体定义成一个模块,不能支持按需加载

解决方式:

  1. 使用支持ESM模块导出的Gulp进行打包。
  2. 放弃或限制Babel的使用,毕竟能导入ESM代码的项目应该是支持ES6语法的。
  3. 不支持ES6模块引入的项目,我们继续采用上述的webpack打包方式。