最后
总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
-
通过
import
导入,通过export
导出 -
静态分析——你可以决定编译时的导入和导出(静态),你只需要看源码,不需要执行它
-
由于 ES6 支持静态分析,因此摇树优化是可行的
-
始终获取实际值,以便实时更改模块本身
-
比 CommonJS 有更好的循环依赖管理
现在,我们了解了不同类型的 JS 模块系统以及它们如何演变。
尽管所有工具和现代浏览器都支持 ES modules,但我们在发布库时不知道用户如何利用我们的库。因此,我们必须确保我们的库在所有环境中都能正常工作。
让我们深入研究并设计一个示例库,更好地回答与发布库有关的所有问题。
我已经建立了一个小型的 UI 库(你可以在 GitHub 上找到源代码),并且我将分享我在编译,打包和发布中的所有经验和探索。
目录结构
在这里,我们有一个小的 UI 库,其中包含 3 个组件:Button,Card 和 NavBar。让我们一步步进行编译并发布。
发布前的最佳实践
1. 摇树优化(Tree Shaking)
webpack 官方文档有说明
-
摇树优化是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如
[import][4]
和[export][5]
。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。新的 webpack 4 正式版本,扩展了这个检测能力,通过package.json
的"sideEffects"
属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是纯的 ES2015 模块,由此可以安全地删除文件中未使用的部分。 -
webpack 和 Rollup 都支持摇树优化,这意味着我们需要牢记某些事情,以便我们的代码可被 Tree Shaking。
2. 发布所有模块形态
-
我们应该发布所有模块形态,例如 UMD 和 ES Module,因为我们永远不知道用户在哪个版本的浏览器或 webpack 中使用此库/包。
-
即使所有打包程序(如 webpack 和 Rollup)都能解析 ES Module ,但如果我们的使用者使用的是 webpack 1.x,则它无法解析 ES 模块。
// package.json
{
“name”: “js-module-system”,
“version”: “0.0.1”,
-
package.json 文件的 main 字段通常用于指向 UMD 版本的库/包。
-
package.jso 文件的 module 字段用于指向 ES 版本的库/包。
鲜为人知的事实:webpack 使用 resolve.mainfields 确定检查
package.json
中的哪些字段。
性能提示:由于所有现代浏览器现在都支持 ES 模块,因此也请务必发布 ES 版本的库/包。这样一来,可以减少编译次数,最终可以减少向用户交付的代码。这将提高应用程序的性能。
那么,下一步是什么?编译还是打包?我们应该使用什么工具?啊,这是最棘手的部分!让我们深入研究研究。
webpack vs Rollup vs Babel
这些我们在日常工作中使用的工具,用于承载我们的应用程序/库/软件包。没有它们,我无法想象现代的 Web 开发有多么糟糕。因此,我们无法将它们进行比较 ❌
每种工具都有其自身的优势,并根据使用者的需求达到不同的目的。
现在让我们看一下这些工具:
webpack
webpack 是一个很棒的模块打包工具, 它被广泛接受并且主要用于构建 SPA。它提供了开箱即用的所有功能,例如代码拆分、按需加载、摇树优化等,并且它本身使用的是 CommonJS 模块系统。
RollupJS
RollupJS 还是类似于 webpack 的模块打包器。但是,RollupJS 的主要优点是它遵循 ES6 修订版中包含的代码模块的新标准化格式,因此你可以使用它来打包 ES module variant 的 library/package,但它不支持按需加载。
Babel
Babel 是 JavaScript 的编译器,以将 ES6 代码转换为可在你的浏览器(或服务器)中运行的代码而闻名。请记住,它只是编译而不会打包你的代码。
我的建议:对库使用 Rollup.js,对应用程序使用 webpack。
编译(Babel-ify)源代码还是直接打包源代码
在构建我的 NPM 库时,我花费了大量时间来试图找出该问题(如何编译、如何打包)的答案。我开始挖掘自己的 node_modules,查找所有优秀的库并检查它们的构建系统。
对比 libraries/packages 构建的输出
在查看了不同 libraries/packages 的构建输出之后,我清楚地了解了这些库的作者在发布之前可能会想到的不同策略。以下是我的观察。
如你在上图中所看到的,我已根据它们的特性将这些库/软件包分为两组:
-
UI Libraries-UI 库(
styled-components
,material-ui
) -
Core Packages-核心包(
react
,react-dom
)
你可能已经弄清楚了这两组之间的区别。
UI Libraries
-
有一个 dist 文件夹,该文件夹是针对 ES 和 UMD/CJS 模块系统 的打包和压缩版本。
-
有一个 lib 文件夹,用来存放被编译后的代码。
Core Packages
- 只有一个文件夹,其中包含针对 CJS 或 UMD 模块系统的打包和压缩版本。
但是,为什么 UI Libraries 和 Core Packages 的构建输出有所不同?
UI Libraries
想象一下,如果我们只是发布库的 bundled version 将其托管在 CDN 上,我们的用户将直接在 <script />
标记中使用它。现在,如果使用者只想使用 <Button />
组件,则他们必须加载整个库。另外,在浏览器中,没有可以解决 tree shaking 的打包工具,最终我们会将整个库代码发送给我们的使用者。因此,我们不能像如下代码引入整个库文件。
现在,如果我们只是简单地将 src 转换为 lib 并将该 lib 托管在 CDN 上,那么我们的使用者实际上可以得到他们想要的任何东西而没有任何开销,“代码更少,加载更快” ✅
Core Packages
Core Packages(核心包)永远不会通过 <script />
标记使用,因为它们必须是主应用程序的一部分。因此,我们可以安全地发布这些软件包的构建版本( UMD,ES),并将构建后的系统交给用户使用。
例如,他们可以使用 UMD 而不使用摇树优化,或者如果打包器能够识别并获得摇树优化的好处,则可以使用 ES。
// CJS require
const Button = require(“uilibrary/button”);
// ES import
import {Button} from “uilibrary”;
对于 UI 库
-
当我们针对 es 模块系统构建时,需要 Babel 编译源代码,并将编译后的代码放置在 lib 文件夹中。我们甚至可以将 lib 托管在 CDN 上。
-
当我们针对 cjs/umd 模块系统和 es 模块系统 等多个模块系统构建时,需要 rollup ??? 打包和压缩代码。
下面我们修改 package.json
以指向对应的模块系统。
// package.json
{
“name”: “js-module-system”,
“version”: “0.0.1”,
// for umd/cjs builds
“main”: “dist/index.js”,
// for es build
“module”: “dist/index.es.js”
}
对于 core packages,我们不需要 lib 版本。我们只需要针对 cjs/umd 模块系统和 es 模块系统,使用 rollup 进行 ??? 打包和压缩源代码即可。
提示:对于愿意通过 <script />
标记下载整个库/软件包的用户,我们也可以在 CDN 上托管 dist 文件夹。
我们怎么进行打包
我们应在在 package.json
中为了不同的目的编写不同的脚本。你能在 GitHub 上面找到 Rollup 的一些配置—— rollup config。
// package.json
{
“scripts”: {
“clean”: “rimraf dist”,
“build”: “run-s clean && run-p build:es build:cjs build:lib:es”,
“build:es”: “NODE_ENV=es rollup -c”,
“build:cjs”: “NODE_ENV=cjs rollup -c”,
“build:lib:es”: “BABEL_ENV=es babel src -d lib”
}
}
我们应该发布哪些东西
-
License
-
README
-
Changelog
-
Metadata(
"main"
,"module"
,"bin"
)— package.json -
Control through
package.json
"files"
property
在 package.json
中, "files"
字段是一个数组类型 ,用来表示软件包被当做第三方依赖安装时,都有哪些文件或文件夹需要下载到业务项目中。如果你在数组中加入了一个文件夹,那么在你 npm install
时,文件夹及下面的文件都会被下载。
在我的示例项目中,我在 "files"
中加入了 lib 和 dist 文件夹。
// package.json
{
最后
总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
你在数组中加入了一个文件夹,那么在你 npm install
时,文件夹及下面的文件都会被下载。
在我的示例项目中,我在 "files"
中加入了 lib 和 dist 文件夹。
// package.json
{
最后
总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了
[外链图片转存中…(img-SFuyXxLd-1715670358833)]
[外链图片转存中…(img-WtjmhEqr-1715670358833)]