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来构建,以保证稳定性。

相关推荐
Ten peaches11 分钟前
Selenium-Java版(环境安装)
java·前端·selenium·自动化
心.c23 分钟前
vue3大事件项目
前端·javascript·vue.js
姜 萌@cnblogs32 分钟前
【实战】深入浅出 Rust 并发:RwLock 与 Mutex 在 Tauri 项目中的实践
前端·ai·rust·tauri
蓝天白云下遛狗39 分钟前
google-Chrome常用插件
前端·chrome
多多*1 小时前
Spring之Bean的初始化 Bean的生命周期 全站式解析
java·开发语言·前端·数据库·后端·spring·servlet
linweidong1 小时前
在企业级应用中,你如何构建一个全面的前端测试策略,包括单元测试、集成测试、端到端测试
前端·selenium·单元测试·集成测试·前端面试·mocha·前端面经
满怀10152 小时前
【HTML 全栈进阶】从语义化到现代 Web 开发实战
前端·html
东锋1.32 小时前
前端动画库 Anime.js 的V4 版本,兼容 Vue、React
前端·javascript·vue.js
满怀10152 小时前
【Flask全栈开发指南】从零构建企业级Web应用
前端·python·flask·后端开发·全栈开发
小杨升级打怪中3 小时前
前端面经-webpack篇--定义、配置、构建流程、 Loader、Tree Shaking、懒加载与预加载、代码分割、 Plugin 机制
前端·webpack·node.js