木灵鱼儿

木灵鱼儿

阅读:185

最后更新:2022/08/17/ 2:54:08

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)

死代码的特征如下:

  1. 代码不会被执行,不可到达
  2. 代码的执行结果不会被用到
  3. 代码只会影响死变量(只读不写)

那么我们做个示范,假设我在我的项目中添加了以下代码:

<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的特点:

  1. 只能作为模块顶层的语句出现
  2. import的模块名只能是字符串常量
  3. 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上测试的。

版权申明

本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。

关于作者

站点职位 博主
获得点赞 0
文章被阅读 185

相关文章