vue3 全局api tree-shaking摇树优化
vue3将一些全局性的api作为一个可以被import
引入的功能来使用,至于为什么这么做,我们稍后再谈,具体有哪些api就不一一说明了,我们举个例子就行,比如nextTick
。
在vue2的时候,如果我们需要使用它,就必须通过Vue.nextTick
的方式来调用:
import Vue from 'vue'
Vue.nextTick(() => {
// something DOM-related
})
现在vue3可以这样:
import { nextTick } from 'vue'
nextTick(() => {
// something DOM-related
})
这种方式带来了一种好处,就是tree-shaking,它可以去除代码中的死代码。
死代码(dead-code)
死代码的特征如下:
- 代码不会被执行,不可到达
- 代码的执行结果不会被用到
- 代码只会影响死变量(只读不写)
那么我们做个示范,假设我在我的项目中添加了以下代码:
<template>
<div></div>
</template>
<script lang="ts">
import { defineComponent, nextTick } from "vue";
export default defineComponent({
setup() {
nextTick(() => {
//我啥也没干
});
return {};
},
});
</script>
此时nextTick的使用就是一个死代码,因为他什么也没有做,什么也没有影响。
tree-shaking 摇树
摇树本质就是消除无用的代码,也就是上面所解释的死代码,而我们JavaScript是通过网络进行加载的,所以去除无用的代码是非常有意义的一件事,毕竟文件越小,加载时间就越短,整体执行时间也会更短。
但是,js的摇树处理和传统的DCE(dead code elimination)处理不同,DCE指的是通过编译器在编译时判断哪些代码是不需要的,然后消除这些代码。
js是一个动态类型语言,我们没法在静态的时候去判断哪些代码是不需要的,这个消除也不可能交给浏览器去处理,所以在ES6 Module出来之前,我们是没有太好的办法去处理的。
而我们现在所使用摇树其实都是基于ES6 Module来实现的,这也是为什么vue3会重写全局的api方法,采用import的模式引入使用。
ES6 Module的特点:
- 只能作为模块顶层的语句出现
- import的模块名只能是字符串常量
- import binding是immutable的(引入的绑定是不可变的,非动态引入)
总结下来就是:ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础。
所谓静态分析就是不执行代码,从字面量上对代码进行分析,ES6之前的模块化,比如我们可以动态require一个模块,只有执行后才知道引用的什么模块,这个就不能通过静态分析去做优化。
这是 ES6 modules 在设计时的一个重要考量,也是为什么没有直接采用 CommonJS,正是基于这个基础上,才使得 tree-shaking 成为可能,这也是为什么 rollup 和 webpack 2 都要用 ES6 module syntax 才能 tree-shaking。
现在我们基本上明白了摇树的原理。
情况并没有想象中那么美好
事实上摇树很美好,它很理想的去除了无用代码,但是事实上各大编译器,webpack、rollup这些,并不能很完美的去除,有时候会甚至无效,因为js语言的动态性,使得静态分析是比较困难的,ES Module为摇树带来了希望,但是有时候有些代码是静态分析不了的,没有那么智能,有时候如果强硬的清楚了,可能会带来更加严重的后果,比如打包后项目居然无法运行了,那这就完全本末倒置了,所以有时候摇树并不会生效。
我们只能按照一定的语法要求去书写才能达到效果。
例子
函数消除
我有一个tool.ts文件,它导出3个方法:
tool.ts
export function test1() {
console.log("test1");
}
export function test2() {
console.log("test2");
}
export function test3() {
console.log("test3");
}
但是我们只引入并使用了test1
<script lang="ts">
import { defineComponent } from "vue";
import { test1 } from "./utils/tool";
export default defineComponent({
setup() {
test1();
return {};
},
});
</script>
打包后你会发现,dist目录下的只会有test1
函数,其他两个已经被删除了。
相比较比如无用变量啊,只引入没有使用啊,基本上都可以正常摇树清楚,我目前是在vite上测试的。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据