在webpack中兼容vite遇到的问题以及解决方式

104 阅读5分钟

前言

随着前端项目规模不断扩大以及构建速度的需求日益增长,我们一直在寻找更高效、更快速的开发工具。Webpack作为一款成熟的模块打包工具,在过去几年里一直是行业标准,它功能强大且高度可配置,但在大型项目中的启动时间和构建速度逐渐成为瓶颈。相比之下,Vite凭借其基于ES模块的原生支持和高效的热更新机制,在开发阶段提供了近乎瞬时的启动速度和流畅的编辑体验。

本文将探讨从Webpack迁移到Vite的原因、过程及注意事项,帮助团队在保证项目稳定性的前提下,提升开发效率和体验。

vite是什么?

Vite是一个利用现代浏览器特性,提供快速开发服务器启动和即时模块热更新的前端构建工具。

为什么选择Vite

  • 速度优势:Vite减少了不必要的打包和重新打包时间,实现快速的项目启动和热更新。
  • 现代化支持:完美支持Vue、React等现代前端框架,并且对TypeScript有内置支持。
  • 简化的配置:与传统工具相比,Vite的配置更简洁直观。

vite核心原理

原生 ESM 支持

Vite 利用了现代浏览器对 ECMAScript 模块 (ESM) 的原生支持,这意味着它可以动态地按需加载模块,而不是在开发环境中预先打包所有的资源。 当浏览器遇到 import 语句时,它会发起一个 HTTP 请求来获取对应的模块文件。

HTTP 服务器

Vite 启动了一个 HTTP 服务器(通常使用 Koa 或类似的轻量级框架)来监听这些请求。 服务器端接收到请求后,会根据需要处理或转换文件,然后将处理后的文件以 ESM 格式返回给浏览器。

按需编译 Vite

只处理那些实际被引用的模块,这使得开发服务器能够非常快速地启动并响应请求。 这种策略减少了不必要的工作,提高了开发效率。

热模块替换 (HMR)

Vite 实现了一种高效的热模块替换机制,可以在不刷新整个页面的情况下更新模块。 HMR 的速度不会随着项目的规模增长而减慢,这是因为 Vite 采用了特殊的算法和技术来确保只更新必要的部分。

静态资源处理

对于静态资源如 CSS 和图片文件,Vite 也会进行优化处理,例如自动添加哈希值来避免缓存问题。

Vue 文件支持

对于 .vue 文件,Vite 提供了专门的支持,包括预编译模板和样式处理等功能。

生产构建

对于生产环境,Vite 使用 Rollup 来打包项目,生成优化过的静态资源。 生产构建会进行代码分割、压缩以及其他优化措施,以确保最终输出的文件体积小且加载速度快。

问题总结

1. 找不到文件

解决方式:忽略后缀extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']

2. require不可用

解决方式:代码中使用require的地方改成import,或使用插件vite-plugin-require-transform

3. ~@找不到路径

解决方式:使用别名

alias: {
	'@': resolve(__dirname, './src'),
	'~@': resolve(__dirname, './src'),
}
4. 找不到环境变量

解决方式:配置定义变量

define: {
  'process.env': process.env,
},
5. vite中无法使用require.context

解决方式:判断打包方式是webpack还是vite,typeof import.meta.webpack !== 'undefined' 时使用require.context,否则使用import.meta.glob代替

6. @import url报错

解决方式:less预处理器中加入额外的less文件

@import url('./animate.less');
@import url('./common.less');
@import url('./global.less');
@import url('./dialog.less');
改成
@import url('@/styles/animate.less');
@import url('@/styles/common.less');
@import url('@/styles/global.less');
@import url('@/styles/dialog.less');

vite.config.ts
  css: {
    preprocessorOptions: {
      less: {
        // 在less预处理器中加入额外的less文件
        additionalData: [
          '@import "@/styles/mixin.less";',
          '@import "@/styles/common.less";',
          '@import "@/styles/global.less";',
          '@import "@/styles/dialog.less";',
        ].join('\n'),
      },
    },
  },
7. vant3样式缺失

解决方式:使用vite-plugin-style-import,需要注意是2.x版本需要安装consola否则报错,1.x是styleImport,2.x是createStyleImportPlugin

createStyleImportPlugin({
  libs: [
    {
      libraryName: 'vant',
      esModule: true,
      resolveStyle: (name) => `../es/${name}/style/index`,
    },
  ],
}),
8. ejs语法加载cdn插入变量
import { ViteEjsPlugin } from 'vite-plugin-ejs'
const ejsData = {
    // ejs读取环境变量
    HDOMAIN_SUFFIX: `'${notPro ? 'cn' : 'com'}'`,
    // CDN外链,ejs会插入到index.html中
    CDNs: {
        js: [
          'js的cdn链接'
        ],
    }
}
ViteEjsPlugin(ejsData),
9. 动态导入警告

解决方式:使用/* @vite-ignore */进行忽略

const getComp = (name: string) => defineAsyncComponent(() => import(/* @vite-ignore */`./components/${name}`));
10. 生产环境移除console.log
terserOptions: {
  compress: {
    // 生产环境时移除console
    drop_console: true,
      drop_debugger: true
  }
}
11. 拆包优化
rollupOptions: {
  output: {
    chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名称
      entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名称
      assetFileNames: '[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片
      manualChunks: (id) => {
      if (id.includes('vant')) {
        return 'vant';
      }
    },
      },
}
12. 部分图片无法加载
<metaname="referrer"content="no-referrer">
13. 压缩代码
viteCompression({
  verbose: true, // 是否在控制台中输出压缩结果
  disable: false,
  threshold: 10240, // 如果体积大于阈值,将被压缩,单位为b,体积过小时请不要压缩,以免适得其反
  algorithm: 'gzip', // 压缩算法,可选['gzip'' brotliccompress ''deflate ''deflateRaw']
  ext: '.gz',
  deleteOriginFile: false, // 源文件压缩后是否删除
}),
14. 打包分析
visualizer({
  open: true, // 注意这里要设置为true,否则无效
  gzipSize: true, // 分析图生成的文件名
  brotliSize: true, // 收集 brotli 大小并将其显示
  filename: 'stats.html', // 分析图生成的文件名
}),
15. rollupOptions.output.globals在打包时声明失效
import externalGlobals from 'rollup-plugin-external-globals';
plugins: [
  externalGlobals({
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    '@antv/f2': 'F2',
    axios: 'axios',
    '@hb/demon-bridge': 'HBridge',
  }),
],
16. 使用pnpm安装运行没问题,但是npm安装运行报错TypeError: fsevents.watch is not a function
"optionalDependencies": { "fsevents": "~2.3.2" }