Webpack迁移Vite采坑指南

前言

本文不介绍什么是webpack、什么是vite,也不分析为什么要迁移。如果你想从webpack迁移到vite,你可能会遇到一些坑,这里我会尽量详细地介绍每一种可能遇到的坑以及解决办法。

老规矩,先说AI的评价:这篇从webpack迁移到vite的采坑指南文章内容全面、详细,主要介绍了在迁移过程中可能遇到的环境变量、路径引入、别名、非ESM包、babel插件等方面的区别和对应处理方法,具有很强的参考价值,是一篇非常优质的技术迁移指南类文章,内容结构完整,准确可靠,可以提供很好的参考价值。我对这篇文章内容和质量表示认可。

1、环境变量

Webpack 通过 DefinePlugin 可以在代码编译时注入环境变量。Vite 需要通过 import.meta 配置来实现。

像 process.env.FOO 这样在 webpack 中可以正常注入并使用的环境变量,在 Vite 构建出的包中会是 undefined。

想在不改变老的业务代码的情况下,实现无侵入的适配vite,就需要通过vite的define配置,这里举个例子:

js 复制代码
    define: {
      // 强烈建议增加一个唯一标识,如果是基于vite开发,基于webpack构建的业务,可以以此来区分做一下适配逻辑
      'process.env.IS_VITE': 'true',
      // 给window上挂上对应的值,还在的在index.html中通过顶部script挂载
      'process.env.VUE_APP_CONFIG_SALT': JSON.stringify(
        env.VUE_APP_CONFIG_SALT
      ),
      'process.env.VUE_APP_CONFIG_ITER': JSON.stringify(
        env.VUE_APP_CONFIG_ITER
      ),
      'process.env.VUE_APP_CONFIG_IV': JSON.stringify(env.VUE_APP_CONFIG_IV),
      'process.env.VUE_APP_CONFIG_ROOTKEY': JSON.stringify(
        env.VUE_APP_CONFIG_ROOTKEY
      ),
      'process.env.BASE_URL': JSON.stringify(env.BASE_URL),
      'process.env.VUE_APP_DEPLOY_ENV': JSON.stringify(env.VUE_APP_DEPLOY_ENV),
      'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV),
      'process.env.ENV': JSON.stringify(env.ENV),
      'process.browser': 'true',
      // 开发时解决buffer里的global,打包build时回到node环境
      ...(env.NODE_ENV === 'production'
        ? {}
        : {
          global: 'window'
        })
    },

基本上window上没有的值,都可以通过此次来挂载,或者在vite的入口index.html中去挂载。

2、路径引入

如果有一个文件是/components/about/index.vue,那么用webpack时,直接写import from '/components/about'即可,但是在vite中,需要写明完整路径。

如果是需要用vite来dev和build,那我建议你直接将对应的业务代码都逐一修改,虽然vite可以通过配置后缀名来解决对.vue的缩写,但是对/index.vue这种缩写是不行的。

如果只是通过vite来dev,还是通过webpack来构建,我也建议将对应的业务代码都逐一修改。

除非是遇到依赖路径是从后端数据库而来的,比如说动态菜单这种。无法有效更加数据库的,这里提供一种侵入性较小的解决方案,就是在入口文件自己定义一个全局的方法:

js 复制代码
window.viteRequire = function(url) {
  // 把一些省略了index的补充上
  const needIndexList = [
    ...
  ]
  needIndexList.forEach(item => {
    if(url == item) {
      url = `${item}/index`
    }
  })
  let url2 = url.includes('.vue') ? `./src/views/${url}` : `./src/views/${url}.vue`
  return () => {
    return import(url2)
    // return import(url2).catch(() => {
    //   return import(url2.replace('.vue', '/index.vue'))
    // })
  }
}

然后通过Vite的标识,在dev时用这个方法来引入模块,并适配对/index.vue的尝试拼接。

3、别名

webpack有一些隐式的别名比如~,这些在vite里可能需要单独显式的声明出来:

js 复制代码
    resolve: {
      alias: {
        '@': path.resolve(__dirname, 'src'),
        '~@': path.resolve(__dirname, 'src')
      }
    },

4、非ESM包的处理

一些第三方库可能需要特殊处理才能与 Vite 正常工作。因为Vite只支持ESM,所以如果引用的包是commonjs的,Vite会自动去做一些转换,如果转换有问题的,请自行配置include或者exclude:

js 复制代码
    optimizeDeps: {
      // 兼容esm和commonjs的default问题
      include: [
        'dayjs',
        'spark-md5',
        'file-saver',
        'photoclip2',
        'vue-infinite-loading'
      ],
      exclude: []
    },

5、babel-plugin-import

如果遇到某些场景,需要做一些import路径的定制化改写,在webpack里一般会用babel-plugin-import,在vite里有对标的插件:

js 复制代码
    plugins: [
      vue(),
      eslint(),
      // 用来处理兼容性
      legacy({
        targets: ['defaults', 'not IE 11']
      }),
      // 兼容babel里的import-plugin,引入组件库使用
      usePluginImport({
        libraryName: '@ccpow/devopslib',
        customName: (name, file) => {
          return `@ccpow/devopslib/packages/components/${capitalize(
            name
          )}/index.vue`
        }
      })
    ],

usePluginImport实现逻辑如下:

js 复制代码
const babelImport = require('babel-plugin-import');
const babel = require('@babel/core');
const importMeta = require('@babel/plugin-syntax-import-meta');

function usePluginImport(options) {

  return {
    name: 'vite-plugin-importer',

    transform(code, id) {
      if (/\.(?:[jt]sx?|vue)$/.test(id) && !/node_modules\/vite/.test(id)) {
        const plugins = [importMeta, [babelImport, options]]

        const result = babel.transformSync(code, {
          ast: true,
          plugins,
          sourceFileName: id,
          configFile: false
        })

        return {
          code: result.code,
          map: result.map
        }
      }
    },
  };
};

export default usePluginImport

6、eslint

在webpack时,默认都是eslint-loader来完成eslint的检测,并在开发时就会提示出来,但是在vite中使用eslint时,默认在开发时不会去校验,所以需要用到插件vite-plugin-eslint

7、对require的hack

如果是基于vite开发,基于webpack来构建,又不想侵入代码里通过require来引入图片的逻辑。可以在全局index.html的入口对require方法进行复写:

js 复制代码
window.require = function(url) {
  if(url.includes('@/')) {
    return new URL(url.replace('@/', '/src/'), import.meta.url).href
  }
  if(url.includes('@img/')) {
    return new URL(url.replace('@img/', '/src/assets/images/'), import.meta.url).href
  }
}

这样才是vite来处理静态资源的方式。

总结

如果是新项目,建议用vite来开发和构建;如果是老项目,建议用vite来开发,还是用webpack来构建,以保证稳定性。

相关推荐
热爱编程的小曾21 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin32 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox