为什么会有这种需求呢?答案是服务端渲染,当服务端渲染时 Vue 的代码是运行在 Node.js 环境的,而非浏览器环境,在 Node.js 环境下资源的模块格式应该是 CommonJS ,简称 cjs 。为了能够输出 cjs 模块的资源,我们可以修改 rollup 的配置: format: 'cjs' 来实现:
其中 module 字段指向的是 vue.runtime.esm-bundler.js 文件,意思就是说如果你的项目是使用 webpack 构建的,那你使用的 Vue 资源就是 vue.runtime.esm-bundler.js ,也就是说带有 -bundler 字样的 ESM 资源是给 rollup 或 webpack 等打包工具使用的,而带有 -browser 字样的 ESM 资源是直接给 <script type="module"> 去使用的。
那他们之间的区别是什么呢?那这就不得不提到上文中的 __DEV__ 常量,当构建用于 <script> 标签的 ESM 资源时,如果是用于开发环境,那么 __DEV__ 会设置为 true ;如果是用于生产环境,那么 __DEV__ 常量会被设置为 false ,从而被 Tree-Shaking 移除。但是当我们构建提供给打包工具的 ESM 格式的资源时,我们不能直接把 __DEV__ 设置为 true 或 false ,而是使用 (process.env.NODE_ENV !== 'production') 替换掉 __DEV__ 常量。例如下面的源码:
为了输出 ESM 格式的资源就需要我们配置 rollup 的输出格式为: format: 'esm' 。
你可能已经注意到了,为什么 vue.esm-browser.js 文件中会有 -browser 字样,其实对于 ESM 格式的资源来说,Vue 还会输出一个 vue.esm-bundler.js 文件,其中 -browser 变成了 -bundler 。为什么这么做呢?我们知道无论是 rollup 还是 webpack 在寻找资源时,如果 package.json 中存在 module 字段,那么会优先使用 module 字段指向的资源来代替 main 字段所指向的资源。我们可以打开 Vue 源码中的 packages/vue/package.json 文件看一下:
可以看到对于顶级调用来说是可能产生副作用的,但对于函数内调用来说只要函数 bar 没有被调用,那么 foo 函数的调用当然不会产生副作用。因此你会发现在 Vue 的源码中,基本都是在一些顶级调用的函数上使用 /*#__PURE__*/ 注释的。当然该注释不仅仅作用与函数,它可以使用在任何语句上,这个注释也不是只有 rollup 才能识别,webpack 以及压缩工具如 terser 都能识别它。
框架应该输出怎样的构建产物
上文中我们提到 Vue 会为开发环境和生产环境输出不同的包,例如 vue.global.js 用于开发环境,它包含了必要的警告信息,而 vue.global.prod.js 用于生产环境,不包含警告信息。实际上 Vue 的构建产物除了有环境上的区分之外,还会根据使用场景的不同而输出其他形式的产物,这一节我们将讨论这些产物的用途以及在构建阶段如何输出这些产物。
不同类型的产物一定是有对应的需求背景的,因此我们从需求讲起。首先我们希望用户可以直接在 html 页面中使用 <script> 标签引入框架并使用:
可以看到,其中并不包含 bar 函数,这说明 Tree-Shaking 起了作用,由于我们并没有使用 bar 函数,因此它作为 dead-code 被删除了。但是如果我们仔细观察会发现, foo 函数的执行也没啥意义呀,就是读取了对象的值,所以它执行还是不执行也没有本质的区别呀,所以即使把这段代码删了,也对我们的应用没啥影响,那为什么 rollup 不把这段代码也作为 dead-code 移除呢?
这就涉及到 Tree-Shaking 中的第二个关键点,即副作用。如果一个函数调用会产生副作用,那么就不能将其移除。什么是副作用?简单地说副作用的意思是当调用函数的时候,会对外部产生影响,例如修改了全局变量。这时你可能会说,上面的代码明显是读取对象的值怎么会产生副作用呢?其实是有可能的,想想一下如果 obj 对象是一个通过 Proxy 创建的代理对象那么当我们读取对象属性时就会触发 Getter ,在 Getter 中是可能产生副作用的,例如我们在 Getter 中修改了某个全局变量。而到底会不会产生副作用,这个只有代码真正运行的时候才能知道, JS 本身是动态语言,想要静态的分析哪些代码是 dead-code 是一件很有难度的事儿,上面只是举了一个简单的例子。
正因为静态分析 JS 代码很困难,所以诸如 rollup 等这类工具都会给我提供一个机制,让我们有能力明确的告诉 rollup :”放心吧,这段代码不会产生副作用,你可以放心移除它“,那具体怎么做呢?如下代码所示,我们修改 input.js 文件:

(编辑:桂林站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|