偶然发现的vue3 tree-shaking 的小秘密

652 阅读3分钟

起因

这个事情的起因是,最近我开源了一个npm包 vue3-tree-shaking可以处理setup语法糖的vue3单文件组件代码字符串,移除script中无用的代码。

然后我就尝试在技术社群分享,希望能跟大家交流一下技术问题(企图混几个star)

发出去不久马上就有同学提出疑问:

内部不就有tree shaking吗?

image.png

当时我只是回复我这边的场景比较少见,是为了处理低代码产生的代码字符串中的冗余信息,并没有深究,后面想想勾起了我的探究欲:

  1. 我这个包除了能减少代码量,是有必要的吗?
  2. 实际vite打包的tree-shaking结果是怎么样的?

探索

1.首先建一个测试工程:

npx create-vite
npm install
npm run dev

image.png

2.然后将初始工程里的src\components\HelloWorld.vue组件稍加改造:

<script setup>
  import { onMounted, reactive, ref } from "vue";
  import { useRoute, useRouter } from "vue-router";
  const route = useRoute();

  defineProps({
    msg: String,
  });
  const emit = defineEmits(["inFocus", "submit"]);

  function buttonClick() {
    emit("submit");
  }
  const count = ref(0);
  const count_useLess = ref(1110);
  const count_useLess2 = ref(22222);
  const state = reactive({
    a: "123",
    b: 666,
  });
  const state_useLess = reactive({
    a: "789",
    b: 10000,
  });
  let useLessVar = "useLessVar";
  const useLessConst = "useLessConst";
  function useLessFn() {
    emit("submit");
  }
  const useLessArrowFn = () => {
    emit("submit");
  };
  let useVar = "useVar";
  console.log("first output");
  defineExpose({
    count,
    buttonClick,
  });
  onMounted(() => {
    let router = "useRouter()";
    count_useLess2.value = "xxxxxx";
    console.log("second output");
  });
</script>

<template>
  <h1 :xx="state.a">{{ msg }} ::{{ useVar }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button>
  </div>
</template>

<style scoped>
  .read-the-docs {
    color: #888;
  }
</style>

直接打包的话会发现,默认是打包成一个js文件的,这样不方便我们观察打包产物

这里稍微修改一下vite.config.js文件:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), vueJsx()],
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes("node_modules")) {
            return "vendor";
          }
        },
      },
    },
  },
});

再次打包,保存借助vscode配置的保存自动格式化:

image.png

这样产物就会划分为两个部分,node_modules下的依赖会输出到单独的文件

仔细观察不难发现,没有用到的顶层/局部 变量/常量如useLessVar,useLessConst,useLessFn,router都没有输出到最终产物


我发现很奇怪的点出现了:

image.png

count_useLess,state_useLess 这两个没有使用到的ref/reactive 只shaking掉了前面的常量值,但是会保留右侧的表达式

想很久没有感觉靠谱的答案,也求教各位佬,猜测,可能是某种情况的无用响应式值会存在“副作用”?

最后

在这个过程里面简单窥探了vue3代码经过vite打包的结果,欢迎讨论下上面的问题

回答一下上面提出的问题,目前看来,在我这个场景下,剔除无用代码也是存在意义的,在vite的基础上继续优化了产物体积,当然这是建立在,我确定这样的移除不会造成任何影响的前提下

最后的最后,推一下我发的npm包,基本使用非常简单: GitHub:github.com/daigang666/… 求个star

npm install vue3-tree-shaking -save-dev
import { treeShakeVueSFC } from 'vue3-tree-shaking';
import vue_sfc from "./test/vue_sfc.vue?raw";

const { code } = treeShakeVueSFC(vue_sfc);
console.log('code :', code);