头图

一、前言

众所周知,Vite作为下一代前端开发与构建工具,就是一个字:快。并且Vite已经作为Vue3默认的构建工具。通过实验表明,项目迁移后,从Vue-cli的近2分钟,到Vite的5秒(项目大小不同,时间也不同),提升了几十倍甚至上百倍的速度。

本文针对老项目从Vue-cli迁移到Vite,提供了全网最全的方案。下面以ant-design-vue-pro为例进行迁移,ant-design-vue版本为1.7.8。

同时,提供了迁移后的仓库,欢迎Star~

GitHub - Seals-Studio/ant-design-vue-pro-vite

关于Element-UI项目的迁移,参考文章:全网最硬核的Element-UI从Vue-cli迁移至Vite(二)

迁移前后对比(参考)

构建工具服务器启动耗时页面首次加载速度 (无缓存)第二次加载速度 (有缓存)热更新 HMR打包
Webpack83s4.78s3.35s4.78s3mins 37s
Vite4.72s (第二次 0.72s)1.71s1.33s瞬间51.45s

二、删除package.json相关依赖

  1. 删除@vue和babel相关

    {
        "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1",
        "@vue/cli-plugin-babel": "^4.5.17",
        "@vue/cli-plugin-eslint": "^4.5.17",
        "@vue/cli-plugin-router": "^4.5.17",
        "@vue/cli-plugin-unit-jest": "^4.5.17",
        "@vue/cli-plugin-vuex": "^4.5.17",
        "@vue/cli-service": "^4.5.17",
        "@vue/eslint-config-standard": "^4.0.0",
        "@vue/test-utils": "^1.3.0",
        "babel-eslint": "^10.1.0",
        "babel-plugin-import": "^1.13.3",
        "babel-plugin-transform-remove-console": "^6.9.4",
    }
  2. 删除loader(webpack插件)和webpack

    {
        "file-loader": "^6.2.0",
        "less-loader": "^5.0.0",
        "vue-svg-icon-loader": "^2.1.1",
        "git-revision-webpack-plugin": "^3.0.6",
        "webpack-theme-color-replacer": "^1.3.26",
    }
  3. 删除babel.conf.jsjsconfig.json
  4. 安装pnpm工具
pnpm是快速的,节省磁盘空间的包管理工具
npm i -g pnpm
# 淘宝源
pnpm config set registry https://registry.npm.taobao.org  
pnpm config set disturl https://npm.taobao.org/dist
pnpm config set NVM_NODEJS_ORG_MIRROR http://npm.taobao.org/mirrors/node  
pnpm config set NVM_IOJS_ORG_MIRROR http://npm.taobao.org/mirrors/iojs  
pnpm config set PHANTOMJS_CDNURL https://npm.taobao.org/dist/phantomjs  
pnpm config set ELECTRON_MIRROR http://npm.taobao.org/mirrors/electron/  
pnpm config set SASS_BINARY_SITE http://npm.taobao.org/mirrors/node-sass  
pnpm config set SQLITE3_BINARY_SITE http://npm.taobao.org/mirrors/sqlite3  
pnpm config set PYTHON_MIRROR http://npm.taobao.org/mirrors/python

三、安装最新版vitevite-plugin-vue2

pnpm add vite vite-plugin-vue2 -D

四、在根目录下新建vite.conf.js

import { defineConfig } from 'vite'
// vue2的vite插件
import { createVuePlugin } from 'vite-plugin-vue2'

export default ({ mode }) => {
  return defineConfig({
    plugins: [
      createVuePlugin({
        jsx: true
      })
    ]
  })
})

五、index.html修改

  • 移动public/index.html到代码根目录(和package.json同级)
  • 在body标签中新增如下:

    <!-- 指明加载main.js -->
    <script type="module" src="/src/main.js"></script>
  • 替换htmlWebpackPlugin插件注入的变量

    htmlWebpackPlugin是webpack插件,所以不能再使用了,vite提供了vite-plugin-html插件来向index.html注入变量
    1. 安装vite-plugin-html

      pnpm add vite-plugin-html -D
    2. 修改vite.config.js,添加配置
       plugins: [
          // ...
          createHtmlPlugin({
            minify: true,
            inject: {
              data: {
                title: 'Ant Design Pro',
                cdn: {
                  css: [],
                  js: [
                        '//cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js',
                        '//cdn.jsdelivr.net/npm/vue-router@3.5.1/dist/vue-router.min.js',
                        '//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
                        '//cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js'
                  ]
                }
              }
            }
          }),
          // ...
        ]
    1. 修改index.html

      • 修改title

        <title><%= title %></title>
      • 修改css和js引入

            <!-- require cdn assets css -->
            <% for (var i in cdn.css) { %>
            <link rel="stylesheet" href="<%= cdn.css[i] %>" />
            <% } %>
        
            <!-- require cdn assets js -->
            <% for (var i in cdn.js) { %>
            <script type="text/javascript" src="<%= cdn.js[i] %>"></script>
            <% } %>

六、环境变量更换

出于安全考虑,vite只能识别以VITE_开头的环境变量了,原VUE_环境变量不生效了,同时,也不能使用process.env.xxx来读取环境变量了。需要修改vite.conf.js配置,手动添加process.env.xxx环境变量
  • 修改vite.conf.js配置,添加环境变量

    import { defineConfig, loadEnv } from 'vite'
    
    export default ({ mode }) => {  
      const env = loadEnv(mode, process.cwd())
      return defineConfig({
        define: {
          'process.env': { ...env }
        },
      })
    })
  • 将所有开头的VUE_环境变量全部替换为VITE_
  • 将所有的process.env.NODE_ENV更改为import.meta.env.MODE
  • 将所有开头为process.env.全部更改为import.meta.env.

七、Ant-Design-Vue按需引入

  1. 安装vite-plugin-style-import插件
# 注意本插件必须采用1.4.1版本,不能采用最新版2.0.0
pnpm add vite-plugin-style-import@^1.4.1 -D
  1. 增加vite.conf.js配置

    plugins: [
        // ...
        styleImport({
            libs: [
              {
                libraryName: 'ant-design-vue',
                esModule: true,
                resolveStyle: (name) => {
                  return `ant-design-vue/es/${name}/style/index`
                },
              }
            ],
          }),
        // ...
    ]

八、Ant-Design-Vue引入moment问题

原因是antdv底层引入采用:import * as moment from "moment";

未兼容ESM写法,参考 github issue: chore: v1 support vite

本文参考[]插件,写了一个vite插件,去修改antdv底层引入moment方式,改为:import moment from moment

  1. 安装依赖包

    pnpm add rollup@">=1.20.0 <2.0.0 || >=2.0.0 <3.0.0" -D
    pnpm add @rollup/plugin-replace -D
  2. 修改vite.conf.js配置
import path from 'path-browserify'
import fs from 'fs'
import replace from '@rollup/plugin-replace'


// 参考vite-plugin-antdv1-momentjs-resolver插件,修改正则表达式,兼容Windows路径
// https://github.com/carl-jin/vite-plugin-antdv1-momentjs-resolver/blob/main/src/index.js
// 将moment_util.js中import * as moment from moment修改import moment from moment
// 原正则表达式
// const antdvDefaultReg = /ant-design-vue\/[\w-\\\/]*\.js$/
// 修改后正则表达式
// const antdvDefaultReg = /ant-design-vue[\/|\\][\w-\\\/]*\.js$/
const AntdMomentResolver = (reg = /ant-design-vue[\/|\\][\w-\\\/]*\.js$/) => {
  return {
    name: 'vite-plugin-antdv1-momentjs-resolver',
    configResolved(config) {
      //  以来预构建时候替换 esbuild
      config.optimizeDeps.esbuildOptions.plugins = config.optimizeDeps.esbuildOptions.plugins
        ? config.optimizeDeps.esbuildOptions.plugins
        : []
      config.optimizeDeps.esbuildOptions.plugins.push({
        name: 'replace-code',
        setup(build) {
          build.onLoad(
            {
              filter: reg,
            },
            (args) => {
              // 首先获取源代码内容
              let source = fs.readFileSync(args.path, 'utf8')
              if (source.indexOf('import * as moment from')) {
                source = source.replace(/import\s\*\sas\smoment\sfrom/g, 'import moment from')
              }
              return {
                contents: source,
              }
            }
          )
        },
      })

      //  添加打包时的替换 rollup
      config.plugins.push(
        replace({
          values: {
            'import * as moment from': (id) => {
              return 'import moment from'
            },
          },
          include: [reg],
          preventAssignment: true,
        })
      )
    },
  }
}

// 引入插件
export default ({ mode }) => {
    return defineConfig({
    plugins: [
          // ...
          AntdMomentResolver(),
          // ...
    ]
   })
}

九、添加代理

  1. 安装path-browserify

    pnpm add path-browserify -D
  2. 添加vite.conf.js配置

        plugin: [],
        // ...
        server: {
          port: 8000,
          //proxy: {
          //  '/api': {
          //    target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
          //    changeOrigin: true,
          //    ws: false,
          //    rewrite: (path) => path.replace(/^\/api/, ''),
          //  }
          //},
        },

十、package.json脚本命令修改

将脚本命令修改为如下:

  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },

十一、postcss配置

  1. 安装插件

    pnpm add postcss autoprefixer -D

十二、添加eslint插件

  1. 安装插件

    pnpm remove eslint eslint-plugin-html eslint-plugin-vue
    pnpm add eslint eslint-plugin-html eslint-plugin-vue eslint-config-prettier eslint-plugin-prettier prettier -D
    # vite-eslint插件
    pnpm add vite-plugin-eslint -D
  2. 添加vite.conf.js配置

    import eslintPlugin from 'vite-plugin-eslint'
    
    export default ({ mode }) => {
        return defineConfig({
        plugins: [
            // ...
            eslintPlugin(),
            // ...
        ]
       })
    }

十三、在写有jsx语法的文件中添加lang="jsx"

<script lang="jsx">
    ...
</script>

十四、添加@别名

修改vite.conf.js配置

export default ({ mode }) => {
    return defineConfig({
       resolve: {
       // ...
         alias: [
           {
              find: /@\/.+/,
              replacement: (val) => {
                  return val.replace(/^@/, path.resolve(__dirname, './src/'))
              },
           },
         ]
       },
    )
}

十五、静态文件引入

  1. 动态组件引入

    const modules = import.meta.glob('../views/**/*.vue')
    
    const currentRouter {
        ...
        // component: constantRouterComponents[item.component || item.key] || (() => import(`/src/views/${item.component}`)),
        component: constantRouterComponents[item.component || item.key] || modules[`../views/${item.component}.vue`],
        ...
    }
    
    
  2. 静态图片引入

    • 直接import图片

      <template>
          <img :src="LogoImg" />
      </template>
      
      <script>
          import LogoImg from '/src/assets/img/logo.svg'
          export default {
              data() {
                  return {
                      LogoImg
                  }
              }
          }
      </script>
    • 采用import.meta.globEager

        1. 图片加载

      <template>

      <img :src="getImg('../../assets/img/log.svg')" />

      </template>

      <script>

      export default {
          methods: {
              getImg(path) {
                  const modules = import.meta.globEager('../../assets/img/*.svg')
                  return modules[path].default
              }
          }
      }

      </script>

      
      * 2. require.context替换
      

      // 修改前
      // const req = require.context('./svg', false, /.svg$/)
      // const requireAll = requireContext => requireContext.keys().map(requireContext)

      // 修改后
      const req = import.meta.globEager('./svg/*.svg')
      const requireAll = (requireContext) => Object.keys(requireContext).map((key) => requireContext[key].default)

    
    ### 十六、其他
  3. 问题:fim.js依赖包引用问题

    解决:删除viser-vue依赖包,可以改用官方G2的封装库@antv/g2plot

    pnpm remove viser-vue
    pnpm add @antv/g2plot
  4. 问题:ant-design-vue组件List引用问题,List.Item为undefined

    解决一:替换代码

     // 替换List组件代码,List.Item为undefined
     if (source.indexOf('Vue.component(List.Item.name, List.Item);')) {
        source = source.replace(
          'Vue.component(List.Item.name, List.Item);',
          'Vue.component("AListItem", Item);'
        )
     }
     if (source.indexOf('Vue.component(List.Item.Meta.name, List.Item.Meta);')) {
        source = source.replace(
          'Vue.component(List.Item.Meta.name, List.Item.Meta);',
           'Vue.component("AListItemMeta", Item.Meta);'
     )
    }               

    解决二:单独引用List.Item


silianpan
160 声望9 粉丝

专注于web前端,spring boot,微服务架构。坚持原创技术分享,为开源贡献力量。