Vite 是新一代的前端开发与构建工具,相比于传统的webpack,Vite 有着极速的本地项目启动速度(通常不超过5s)以及极速的热更新速度(几乎无感知)。
Vite 官网:https://cn.vitejs.dev/

下面分享 Webpack 项目(vue-cli项目)迁移至 Vite 的过程步骤、遇到的问题及解决方式。

一、项目背景

首先不建议做同时支持webpack和vite,这种只适合自己玩玩,实际项目中要考虑多人开发时的协作和维护成本,只用一种最好。

  • 原项目主技术栈及版本:
    ​ Vue@3.2.3
    ​ typescript@4.3.5
    ​ element-plus@1.0.2-beta.70
  • 基于vue-cli@4.5.13
  • 迁移至vite@2.6.4

二、引入Vite

1、package.json

  • scripts里替换命令:
"dev": "vite",
"serve": "vite preview",
"build": "vue-tsc --noEmit && vite build"
  • devDependencies移除依赖:
    在这里插入图片描述
  • 添加依赖:
npm i @vitejs/plugin-vue@1.9.3 vite@2.6.4 vue-tsc@0.3.0 -D

2、vite配置文件

移除vue.config.js,
添加vite.config.ts:

  import { defineConfig } from 'vite'
  import vue from '@vitejs/plugin-vue'
  import path from 'path'
  
  // https://vitejs.dev/config/
  export default defineConfig({
    plugins: [
      vue(),
    ],
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src')
      }
    },
    server: {
      host: '0.0.0.0',
      port: 9000,
      proxy: {},
    },
  })

3、index.html

index.html从public文件夹移动到项目根目录里。
官方说明:https://vitejs.cn/guide/#index-html-and-project-root

4、TS相关

+ tsconfig.json
	tsconfig.json的compilerOptions里添加配置:
	```json
	"isolatedModules": true,
	```
+ `/src/shims-vue.d.ts`(没有就新建):
	```js
	/// <reference types="vite/client" />

	declare module '*.vue' {
	  import type { DefineComponent } from 'vue'
	  const component: DefineComponent<{}, {}, any>
	  export default component
	}
	```
	注意其他第三方的ts声明放入另一个文件`/src/shims-tsx.d.ts`里,和vue-cli模式一致。

5、启动

尝试npm run dev运行:
(十有八九跑不起来,问题往下翻。)

三、不兼容点

1、环境变量

webpack里的构建时环境变量默认存储在process.env里,
而vite是存储在import.meta.env里,

import.meta.env.MODE: {string} 应用运行的模式。
import.meta.env.BASE_URL: {string} 部署应用时的基本 URL。他由base 配置项决定。
import.meta.env.PROD: {boolean} 应用是否运行在生产环境。
import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)

所以项目里用到的相关地方需要修改,例如process.env.NODE_ENV应替换为import.meta.env.MODE。

2、.env文件

  • vue-cli项目的.env文件自定义变量名都是以VUE_APP_开头,
  • vite里需要改成以VITE_开头

3、路由history模式

  • vue-cli项目如果是部署在服务器子目录下,需要配置vue.config.js里的publicPath字段为子目录路径,例如 /h5
  • 而vite里需要配置vite.config.ts里的base字段,且该字段需要以/结尾,例如 /h5/

4、.vue文件的引入方式

vite + ts 里所有.vue文件的引入都必须精确到.vue拓展名,

// 文件路径 src/components/Headers/index.vue

// 原方式
import Headers from '@/components/Headers'

// vite里
import Headers from '@/components/Headers/index.vue'

5、JSX支持

  • webpack项目支持jsx是使用的@vue/babel-plugin-jsx插件,
  • vite里对应的插件是@vitejs/plugin-vue-jsx
    vite.config.ts里需要配置:
    import vueJsx from '@vitejs/plugin-vue-jsx'
    
    plugins: [
      ...,
      vueJsx(),
    ],
    

6、require.context

  • require context 是 webpack 提供的特有的模块方法,用于导入一个目录下的所有文件
    const routes = require.context('./modules', true, /([\w\d-]+)\/routes\.ts/)
        .keys()
        .map(id => context(id))
        .map(mod => mod.__esModule ? mod.default : mod)
        .reduce((pre, list) => [...pre, ...list], [])
    
    export default new VueRouter({ routes })
    
  • vite里需要使用使用 import.meta.globEager
    const routesModules = import.meta.globEager<{default: unknown[]}>('./modules/**/routes.ts')
    const routes = Object
      .keys(routesModules)
      .reduce<any[]>((pre, k) => [...pre, ...routesMod[k].default], [])
    
    export default new VueRouter({ routes })
    

四、第三方插件

一些适用于webpack的第三方插件在vite里肯定不适用了,只能寻找替代方案。

1、svg-sprite-loader

改用vite-plugin-svg-icons

  • main.ts里配置全局注册:
    import SvgIcon from '@/components/SvgIcon/index.vue'
    
    const app = createApp(App)
    app.component('SvgIcon', SvgIcon)
    
  • vite.config.ts里配置plugins:
    plugins: [
      ...,
      svgIcons({
        iconDirs: [path.resolve(process.cwd(), 'src/assets/svgs')],
        symbolId: 'icon-[dir]-[name]'
      })
    ],
    

2、path-to-regexp

  • vue-cli项目里可以直接使用path-to-regexp包,估计是vue-cli有相关依赖
  • vite里需要手动安装,npm i path-to-regexp -S
    最新版是按需引入方式import { pathToRegexp } from 'path-to-regexp'

3、autoprefixer

如果项目配置了autoprefixer,也需要手动安装依赖npm i autoprefixer -D
另外vite也能自动读取postcss.config.js配置:

	module.exports = {
	  plugins: [
	    require('autoprefixer'),
	  ]
	}

4、path模块

vite里在运行在浏览器端的业务代码里不支持直接使用path模块书写路径,需要使用path-browserify

// 原方式
import path from 'path'
// vite里(npm i path-browserify -S)
import path from 'path-browserify'

5、全局less引入

  • webpack里可以通过配置style-resources-loader插件来引入一个less文件作为全局less自动导入,从而在任意.vue文件的<style>标签里直接使用定义的less变量或less混入等。
  • vite里可以配置vite.config.ts:
    css: {
        preprocessorOptions: {
          less: {
            additionalData: `@import "${path.resolve(__dirname, 'src/styles/variable.less')}";`,
            javascriptEnabled: true,
          }
        }
    },
    
    另外,less定义的变量在定义路径时不能使用alias快捷标识,需要改用绝对路径:
    // 原方式
    @imgPath: '~@/assets/images';
    // vite里
    @imgPath: '/src/assets/images';
    

五、其他报错问题

1、打包时@charset警告

这个和element-plus使用的@charset配置有关,也可能是其他第三方依赖使用的,总之vite对@charset的使用做了更严格的校验。

解决方法:配置让vite忽略警告。

  • postcss.config.js
    module.exports = {
      plugins: [
        require('autoprefixer'),
        // 移除打包element时的@charset警告
        {
          postcssPlugin: 'internal:charset-removal',
          AtRule: {
            charset: (atRule) => {
              if (atRule.name === 'charset') {
                atRule.remove()
              }
            }
          }
        },
      ]
    }
    

2、pre-commit失效问题

项目配置husky + pre-commit 钩子,在git commit时进行代码lint校验,迁移vite后竟然失效了。

原项目用的是typescript@4.3.5 husky@4.3.8,解决步骤:

  • 自定义lint命令
    • vue-cli项目的lint是直接运行vue-cli-service lint --fix就行
    • vite里就得自己写了,
      "scripts": {
         	"lint": "eslint . --ext .js,.jsx,.vue,.ts,.tsx --fix"
      },
      "husky": {
          "hooks": {
            "pre-commit": "lint-staged"
          }
        },
        "lint-staged": {
          "*.{js,jsx,vue,ts,tsx}": [
            "npm run lint"
          ]
        },
      
  • 锁定依赖版本
    • 最终发现是依赖版本问题,package.json里的依赖版本在npm i后被覆盖了,可能是vite相关依赖覆盖掉了,查找package.lock.json文件可以验证。
    • 解决方式就是手动再单独安装一次依赖:
      npm i typescript@4.3.5 husky@4.3.8 -D
      
    • 注意package.lock.json不要配置.gitignore忽略,这是版本锁定文件,具体就不再赘述。
  • yorkie相关报错
    yorkie是配合husky使用的模块,遇到报错时需要手动安装依赖:
    bash npm i yorkie -D

六、结语

本来迁移不太复杂的,但由于项目使用了ts和eslint,代码校验比较严格,这使迁移工作至少增加了一倍的工作量,但时间花费是值得的,严谨的代码和规范才能保证项目持续稳定的运行和维护。

附录:

  • vite.config.ts 完整代码:
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import path from 'path'
    import svgIcons from 'vite-plugin-svg-icons'
    import vueJsx from '@vitejs/plugin-vue-jsx'
    
    // https://vitejs.dev/config/
    export default defineConfig({
      base: '/yj99admin/',
      resolve: {
        alias: {
          '@': path.resolve(__dirname, './src')
        }
      },
      server: {
        host: '0.0.0.0',
        port: 9000,
        proxy: {},
      plugins: [
        vue(),
        svgIcons({
          iconDirs: [path.resolve(process.cwd(), 'src/assets/svgs')],
          symbolId: 'icon-[dir]-[name]'
        }),
        vueJsx(),
      ],
      css: {
        preprocessorOptions: {
          less: {
            additionalData: `@import "${path.resolve(__dirname, 'src/styles/variable.less')}";`,
            javascriptEnabled: true,
          }
        }
      },
    })
    

Logo

前往低代码交流专区

更多推荐