Vue Cli 项目重构为 Vite

855 阅读2分钟

Vue Cli 项目重构为 Vite

我们知道 VueCli 使用的是 webpack 打包工具,而 Vite 是基于 ESM 的打包工具,所以我们可以使用 Vite 来替换 VueCli,来提升我们的开发体验。

更新依赖与 package.json

我们先将项目中的 VueCli 相关依赖删除,然后安装 Vite 相关依赖。

包管理工具我们使用 pnpm,如果你使用的是 npmyarn,请自行替换。

  1. 删除 VueCli 相关依赖
pnpm remove @vue/cli-plugin-babel @vue/cli-plugin-eslint @vue/cli-service
  1. 安装 Vite 相关依赖

如果你用的是 vue2.6, 请安装 vite-plugin-vue2, 并且不需要 jsx 支持

pnpm add -D vite @vitejs/plugin-vue @vitejs/plugin-vue2 @vitejs/plugin-vue2-jsx
  1. 同时我们可以移除 babel 相关依赖
pnpm remove -D babel-plugin-dynamic-import-node babel-eslint
  1. 修改 scripts

修改 package.json 中的 scripts

{
  "scripts": {
    "dev": "vite --host",
    "build:prod": "vite build",
    "build:stage": "vite build --mode staging"
  }
}

配置 Vite

vite 的配置文件是 vite.config.js,我们可以在项目根目录下创建该文件。

  1. 创建 vite.config.js,并添加基本配置
import { defineConfig, loadEnv } from 'vite';
// 如果使用的是 vue2.6, 请使用 vite-plugin-vue2
// import { createVuePlugin as vue } from 'vite-plugin-vue2';
import vue from '@vitejs/plugin-vue2';
import { resolve } from 'path';
import rollupNodePolyFill from 'rollup-plugin-node-polyfills';
import { dataToEsm } from '@rollup/pluginutils';
import vueJsx from '@vitejs/plugin-vue2-jsx';

export default ({ mode }) => {
  process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') };

  return defineConfig({
    plugins: [
      // vue2.6
      // vue({ jsx: true }),
      // vue2.7
      vue(),
      vueJsx(),
    ],
    server: {
      port: 75,
      proxy: {
        [process.env.VITE_APP_BASE_API]: {
          target: process.env.VITE_APP_PROXY_API,
          changeOrigin: true,
          rewrite: (path) => path.replace(new RegExp(`^${process.env.VITE_APP_BASE_API}`), ''),
        },
      },
    },
    resolve: {
      alias: {
        '@': resolve('src'),
      },
      extensions: ['.js', '.vue', '.json'],
    },
  });
};
  1. 如果你的项目中使用了 node 的一些模块,需要在 resolve.alias 中添加对应的 polyfill

需要添加 rollup-plugin-node-polyfills 依赖

defineConfig({
  // ...
  resolve: {
    alias: {
      '@': resolve('src'),
      // 如果你的项目中使用了node的一些模块,需要在这里添加对应的polyfill,如没有配置.js的extensions,需要在末尾添加.js
      path: 'rollup-plugin-node-polyfills/polyfills/path',
  }
})
  1. 如果你的项目需要使用 build 的配置,可以参考如下配置
defineConfig({
  // ...
  build: {
    rollupOptions: {
      output: {
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash][extname]',
        // 分离依赖包
        manualChunks: {
          vue: ['vue'],
          echarts: ['echarts'],
          'element-ui': ['element-ui'],
        },
      },
    },
  },
});
  1. 移除 vue.config.js
  2. 若你的项目中使用了 jsx,请将 '.js' 文件修改为 '.jsx'

全局替换 Env 变量与 require

  1. 将所有的 process.env 替换为 import.meta.env,如:
// 替换前
const uploadFileUrl = process.env.VUE_APP_BASE_API + '/upload';
// 替换后
const uploadFileUrl = import.meta.env.VITE_APP_BASE_API + '/upload';

process\.env\.VUE_APP_ replace import.meta.env.VITE_APP_

请勿将 vite.config.js 中的 process.env 替换为 import.meta.env

  1. 将所有直接导入的 require 替换为 import,如:
// 替换前
const { version } = require('../../package.json');
// 替换后
import { version } from '../../package.json';

const\s+\{(.+?)\}\s+=\s+require\((.+?)\); replace import {$1} from $2;

  1. 移除动态导入的 require.context,如:
// 替换前
const svgIcons = require.context('@/assets/icons/svg', false, /\.vue$/);
// 替换后
const svgIcons = import.meta.globEager('@/assets/icons/svg');
  1. 动态导入的 Vue 路由组件需要调整为 import(),如:
// 替换前
route.component = () => require([`@/views/${route.component}`]);
// 替换后

// 动态导入
const views = import.meta.glob('@/views/**/**.vue');
route.component = views[`@/views/${route.component}.vue`];

// 确定文件路径的动态导入
// 替换前
route.component = () => require('@/views/index/index');
// 替换后
route.component = () => import('@/views/index/index.vue');

移除 style 中的 alias 导入

由于 vite 并不支持 sass/lessalias 导入方式,所以需要将 style 中的 alias 导入替换为 vite 所支持的格式,如:

<!-- 修改前 -->
<style lang="scss" scoped>
@import '~@/assets/styles/variables.scss';
</style>

<!-- 修改后 -->
<style lang="scss" scoped>
@import '@/assets/styles/variables.scss';
</style>

禁用 CSS 注入页面,自动注入 CSS 内容的行为需要通过 ?inline 参数来关闭:

// 修改前
import './foo.css';

// 修改后
import './foo.css?inline';

调整 ESLint 配置

因为我们不再需要 babel 来支持 eslint 解析代码,所以我们需要移除 babel-eslint parser 配置:

// .eslintrc.js
module.exports = {
  // ...
  // 移除parser
  // parser: 'babel-eslint',
  // ...
};

调整 index.html

在一个 Vite 项目中,index.html 是该 Vite 项目的入口文件

移动 public/index.htmlindex.html,并添加 JavaScript 模块 的引入方式

<!-- ... -->
<script type="module" src="/src/main.js"></script>
<!-- ... -->

<script>标签一般添加在 </body>

SVG 支持

动态导入 SVG 图标,需要配合使用 vite-plugin-svg-icons 插件来实现:

  1. 安装依赖
pnpm add -D vite-plugin-svg-icons
  1. 修改 vite.config.js,参考配置如下:
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';

defineConfig({
  // ...
  plugins: [
    // ...
    createSvgIconsPlugin({
      // 图标文件夹中,所有的svg文件将被转换为svg精灵
      iconDirs: [resolve(process.cwd(), 'src/assets/icons/svg')],
      // 指定symbolId格式
      symbolId: 'icon-[dir]-[name]',
    }),
  ],
});
  1. 添加虚拟模块至入口文件,如:
// src/main.js
// ...
import 'virtual:svg-icons-register';
// ...
  1. 添加 SvgIcon/index.vue 组件
<template>
  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
import { isExternal } from '@/utils/validate';

export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true,
    },
    className: {
      type: String,
      default: '',
    },
  },
  computed: {
    isExternal() {
      return isExternal(this.iconClass);
    },
    iconName() {
      return `#icon-${this.iconClass}`;
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className;
      } else {
        return 'svg-icon';
      }
    },
    styleExternalIcon() {
      return {
        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`,
      };
    },
  },
};
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>

总结

至此,我们已经完成了 vue-cli 项目的迁移,接下来我们可以使用 vite 的各种优势了。

如果遇到构建产物无法正常使用的情况,请在你的动态路由中导入 import.meta.glob,如这里

参考