NPM 组件你应该知道的事 亦凉 2022-12-11 09:25 289阅读 0赞 开发一个 npm 组件, 你是否了解需要对外导出什么格式的代码?如何让 npm 组件体积尽可能小? 整篇文章按照如下目录进行讲解: * 为何需要打包 * 组件打包输出格式 * 如何打包 esm 模式代码(感兴趣选读) * 减少组件打包体积的最佳实践 ## 为何需要打包 ## 首先,这里的打包概念解释一下, 只要有输出到新目录,就称为打包(免得大家对打包理解概念不一致)。 * 一份代码,多种消费方式 * 使用新特性语法,由于一般项目中,会默认不对 node\_module 中的库进行编译以提高整个项目的编译速度,所以作为 npm 包,要转换成 es5 ,免得消费方吐槽…… ## 打包格式 ## 按照目前主流的模块系统来区分,可以先看一张图片宏观了解一下:![format_png][] ### esm ### 如果是用 npm 组件来使用, 都推荐使用这种导出模式。 ##### 产生方式: ##### * rollup 声明 target 为 esm 或者 babel 编译之后生成一个新的目录 (iceworks 的做法) * package.json 中声明 module,指向 esm ##### 使用方式: ##### * 浏览器通过 `<script type="module" />` 引入 * 作为 npm 使用 ##### 特性 ##### 由于是静态的,所以可以使用 tree-shaking ### umd ### ##### 使用方式 ##### * 浏览器通过 `<script />` 引入 * 浏览器通过 requirejs 或 seajs 引入 【目前这个已经很少使用了】 ##### 如何产生 ##### * rollup 或者 webpack 声明 target 为 umd * package.json 中声明 unpkg,指向对应文件 ### commonjs ### ##### 使用方式 ##### * node 端, npm 方式 ##### 如何产生 ##### * rollup 或者 webpack 声明 target 为 commonjs * package.json 中声明 main,指向对应文件 。 > package.json 中引用优先级如下:target 为 web 时, 依次查找 browser、module 和 main。其他 target , 依次找 module 和 main。因此如果声明了 module, 会优先读取 module 中的路径。 因此, 在导出的时候,同时设置好 main 和 module 字段,这样就可以二者兼具了,在node端,浏览器端都可以正常使用。 ## webpack 如何打包 esm 模式 ## 这里不讲 rollup , 毕竟写一个 target 就可以解决了。 大家都知道,webpack 的 target 没有支持 esm 模式, 而 rollup 提供了, 为此很多人也在吐槽,为什么 webpack 不做…… 我们这使用的是 iceworks , 源码地址\[1\],它默认支持导出 esm 模式, 那就一起看看它的源码是怎么做的。它是用 build-plugin-component 这个来实现的。【贴部分源码,感兴趣的可以看看】 * 如果不是 jsx 或 tsx 文件, 则直接 copy 到目标目录,否则经过 bable 处理, 并将后缀改成 js ![format_png 1][] jsx.png * 使用 babel-plugin-import 处理第三方依赖的组件库,且兼容没有 es 模块的第三方组件 ![format_png 2][] ba.png * 将 ts 解析生成 d.ts 文件 ![format_png 3][] d.png * bable 7 (@babel/preset-env ),若为 esm 模块, 则关闭 module 选项 ![format_png 4][] ![format_png 5][] 其实它实现的很简单, 如果是 es 模块, 只是用 babel 将对应的 es6 语法编译成 es5 语法(且不选择modlue), 然后 copy 到新目录 es 下, 对于里面使用到的第三方依赖组件, 用 babel-plugin-import 做一下兼容处理。 ## 组件打包体积的最佳实践 ## 首先,尽可能提供 esm 的格式, 因为它可以走 tree-shaking ,摇掉不必要的文件。【webpack 只要开启 production 模式,就默认有 tree-shaking 功能】 #### tree-shaking #### ![format_png 6][] tree.png ##### 定义 ##### 如果被标记为无副作用的模块没有被直接导出使用,打包工具会跳过进行模块的副作用分析评估。由此安全地删除文件中未使用的部分。 在打包阶段,webpack无法准确判断某个文件是否有副作用,所以默认认为所有文件都是有副作用的。也就是说这里sideEffects默认是true。 > 副作用:一个函数会、或者可能会对函数外部变量产生影响的行为。 * 模块作用域 将package.json 中sideEffects 设置为 false ,则表示改模块全部忽略副作用 * 局部文件 package.json 中 sideEffects 数组写对应文件,比如常见的写上 css 文件, 如 antd 的配置![format_png 7][] * 函数级别 `/*@__PURE__*/` 声明函数无副作用 **只要我们基本保证这个组件包没有对外部对象产生影响,就能设置 sideEffects: false 了** 举个栗子:设置了 sideEffects: false 和 未设置 sideEffects: false 的情况如下, 可以看到体积确实减少了不少 ![format_png 8][] ![format_png 9][] image.png 更深入的 sideEffect 可以看看这篇文章\[2\]: #### peerDependency #### 对于消费方可能也用到的组件,写到 peerDependency 中。看下面一张图就可以理解。这样可以减少重复打包。![format_png 10][] 举一个栗子: "peerDependencies": { "react": ">=16.12.0", "react-dom": ">=16.12.0" } > 如上的配置,可以让组件库下的 node\_modules 不安装 react,同时指定组件库使用方需安装的 react/reactDOM 的版本。 #### external #### 对于打包成 umd 的文件,由于它无法分析是否存在 peerDependencies, 所以如果使用方已有 react、 react-dom 等库,需要在webpack打包时,将 external 剔除掉对应依赖。 #### wepack5 模块联邦 #### external 还是静态的,如果项目支持, 还可以使用 wepack5 Module Federation 方式,由使用方动态决定是否下载依赖。 ## 总结 ## 1. 对外提供组件时,同时提供 esm ,commonjs, umd 这3种方式,并且在package 中对应的字段进行声明,以确保这个包可以兼容多环境。 2. 尽可能提供 esm 模式,并且如果这个组件没有影响外部变量时,设置 sideEffect 为 false, 让使用方可以最大的 tree-shaking 。对于公用的依赖包,将其写入 peerDependencies 中。 3. 若要提供 umd 模式, 在打包时, 将对应公用依赖写入 external 剔除对应依赖。 ### 参考资料 ### \[1\] 源码地址: *https://github.com/ice-lab/iceworks* \[2\] 文章: *https://zhuanlan.zhihu.com/p/40052192* ## 关于奇舞周刊 ## 《奇舞周刊》是360公司专业前端团队「奇舞团」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。 ![format_png 11][] [format_png]: /images/20221123/b7b06d644b504a14b40035a03db2f80d.png [format_png 1]: /images/20221123/6c3276c6e5054174bcf9fb17404cd41c.png [format_png 2]: /images/20221123/57ac73fb773f4e3d9faf9cadaf60120a.png [format_png 3]: /images/20221123/cd664a09c6bf4d24acceb359fc1c84fe.png [format_png 4]: /images/20221123/6cdefb75d59a47b89274798695d0baef.png [format_png 5]: /images/20221123/691f99e445864900980661d4adbf2fd6.png [format_png 6]: /images/20221123/f5f8df71190243c5b3518d89c2af00a5.png [format_png 7]: /images/20221123/e9f87ee71638408da3c1076774540988.png [format_png 8]: /images/20221123/73bf9fdf852147f19d97920ae5055416.png [format_png 9]: /images/20221123/c98d2b7fd5544788af6cba550356ae81.png [format_png 10]: /images/20221123/0236a5a9cfc243b2af0be7be0d170ae3.png [format_png 11]: /images/20221123/528d14cab4c74ac182d8ed6e7df27d61.png
还没有评论,来说两句吧...