You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vite构建ES6代码不完整,导出内容丢失问题排查

Vite打包后JS导出内容丢失,仅保留console.log输出

我正在搭建一个仓库,用于将HTML、CSS和JS转译为可在项目中导入的模块,但Vite无法正确转译JS文件——输出脚本中缺少我编写的部分代码。

我的配置与代码

Vite配置(临时禁用minify以查看可读内容)

import { defineConfig } from 'vite'
import { resolve } from 'path'

import { ViteMinifyPlugin } from 'vite-plugin-minify'

export default defineConfig({
  copyPublicDir: false,
  build: {
    minify: false,
    rollupOptions: {
      input: {
        footer: resolve(__dirname, 'src/footer/footer.html'),
        navbar: resolve(__dirname, 'src/navbar/navbar.html'),
      },
      output: {
        entryFileNames: `assets/[name].js`,
        chunkFileNames: `assets/[name].js`,
        assetFileNames: `assets/[name].[ext]`
      }
    },
  },
  plugins: [
    // ViteMinifyPlugin({})
  ]
})

测试用JS代码

function loadUserInfo() {
  return {
    email: "janedoe@abc.com",
    name: "Jane Doe",
    username: "janedoe"
  }
}

const world = "hello world"

console.log("testing")

export { loadUserInfo, world }

转译后的JS代码

(function polyfill() {
  const relList = document.createElement("link").relList;
  if (relList && relList.supports && relList.supports("modulepreload")) {
    return;
  }
  for (const link of document.querySelectorAll('link[rel="modulepreload"]')) {
    processPreload(link);
  }
  new MutationObserver((mutations) => {
    for (const mutation of mutations) {
      if (mutation.type !== "childList") {
        continue;
      }
      for (const node of mutation.addedNodes) {
        if (node.tagName === "LINK" && node.rel === "modulepreload")
          processPreload(node);
      }
    }
  }).observe(document, { childList: true, subtree: true });
  function getFetchOpts(link) {
    const fetchOpts = {};
    if (link.integrity)
      fetchOpts.integrity = link.integrity;
    if (link.referrerPolicy)
      fetchOpts.referrerPolicy = link.referrerPolicy;
    if (link.crossOrigin === "use-credentials")
      fetchOpts.credentials = "include";
    else if (link.crossOrigin === "anonymous")
      fetchOpts.credentials = "omit";
    else
      fetchOpts.credentials = "same-origin";
    return fetchOpts;
  }
  function processPreload(link) {
    if (link.ep)
      return;
    link.ep = true;
    const fetchOpts = getFetchOpts(link);
    fetch(link.href, fetchOpts);
  }
})();
console.log("testing");

可以看到,只有console.log("testing")被保留,loadUserInfo和world的导出完全丢失。

更新情况

禁用Tree Shaking后问题暂时解决,但重新启用minify后,导出内容再次被移除:

(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))n(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&n(o)}).observe(document,{childList:!0,subtree:!0});function s(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function n(e){if(e.ep)return;e.ep=!0;const r=s(e);fetch(e.href,r)}})();console.log("testing");

问题原因

这是Vite底层依赖的Rollup以及压缩工具(Terser/esbuild)的死代码消除机制导致的:
你的loadUserInfo和world虽然被export,但在当前入口(HTML文件)里没有被任何代码引用,工具会判定它们是"未使用的死代码",在打包或压缩时自动移除。

禁用Tree Shaking能解决是因为关闭了Rollup的死代码检测,但开启minify后,压缩工具同样会执行死代码消除,所以问题复现。

解决方案

1. 标记文件为有副作用

在项目根目录的package.json中添加配置,告诉Vite这些JS文件存在副作用,不能随意移除导出内容:

"sideEffects": ["./src/**/*.js"]

如果只有特定文件需要保留,可以指定具体路径;如果整个项目都需要,直接设为true即可。

2. 在HTML中显式引用导出内容

如果你的JS是通过HTML作为入口打包的,可以在HTML里添加模块脚本,主动引用导出的内容,避免被判定为死代码:

<script type="module">
  // 根据实际路径调整
  import { loadUserInfo, world } from './assets/footer.js';
  // 无需实际使用,只要引用即可触发工具保留导出
  void loadUserInfo;
  void world;
</script>

3. 针对性关闭死代码消除

如果需要保留Tree Shaking和minify的其他优化,可以单独关闭死代码消除逻辑:

  • 关闭Rollup的Tree Shaking:
export default defineConfig({
  build: {
    rollupOptions: {
      treeshake: false,
      // ...其他配置
    },
  },
});
  • 关闭Terser的未使用代码消除(如果用的是terser作为minify工具):
export default defineConfig({
  build: {
    minify: 'terser',
    terserOptions: {
      compress: {
        unused: false, // 禁用未使用代码消除
      },
    },
    // ...其他配置
  },
});

注意:esbuild的minify目前没有细粒度关闭死代码消除的选项,若使用esbuild则需要切换为terser。

4. 调整导出方式

可以改用export default导出对象,确保工具不会误判为死代码:

// 使用default导出
export default {
  loadUserInfo: function() {
    return {
      email: "janedoe@abc.com",
      name: "Jane Doe",
      username: "janedoe"
    }
  },
  world: "hello world"
}

内容的提问来源于stack exchange,提问作者nayiaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 14:22:24