全網最硬核的Element-UI從Vue-cli遷移至Vite(二)

silianpan 發表於 2022-05-26
Vue

一、前言

前不久,對ant-design-vue-pro專案進行了遷移,參考文章:全網最硬核的Ant-Design-Vue從Vue-cli遷移至Vite(一),遷移後的專案地址:GitHub - Seals-Studio/ant-design-vue-pro-vite

本期針對Element-UI庫進行遷移,以vue-element-admin專案為例進行遷移,element-ui版本為2.13.2。同時,提供了遷移後的倉庫,歡迎Star~

GitHub - Seals-Studio/vue-element-admin-vite

二、背景

眾所周知,Vite作為下一代前端開發與構建工具,就是一個字:快。並且Vite已經作為Vue3預設的構建工具。通過實驗表明,專案遷移後,從Vue-cli的近2分鐘,到Vite的5秒(專案大小不同,時間也不同),提升了幾十倍甚至上百倍的速度。

遷移前後對比(參考)

構建工具伺服器啟動耗時頁面首次載入速度 (無快取)第二次載入速度 (有快取)熱更新 HMR打包
Webpack83s4.78s3.35s4.78s3mins 37s
Vite4.72s (第二次 0.72s)1.71s1.33s瞬間51.45s

三、刪除package.json相關依賴

  1. 刪除@vue和babel相關

    {
        "@vue/cli-plugin-babel": "4.4.4",
        "@vue/cli-plugin-eslint": "4.4.4",
        "@vue/cli-plugin-unit-jest": "4.4.4",
        "@vue/cli-service": "4.4.4",
        "@vue/test-utils": "1.0.0-beta.29",
        "babel-eslint": "10.1.0",
        "babel-jest": "23.6.0",
        "babel-plugin-dynamic-import-node": "2.3.3",
    }
  2. 刪除loader(webpack外掛)和webpack

    {
        "html-webpack-plugin": "3.2.0",
          "script-ext-html-webpack-plugin": "2.1.3",
        "sass-loader": "8.0.2",
        "svg-sprite-loader": "4.1.3",
    }
  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: 'vue Element Admin',
                cdn: {
                  css: [],
                  js: [
                        '//cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js',
                        '//cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js',
                        '//cdn.jsdelivr.net/npm/[email protected]/dist/vuex.min.js',
                        '//cdn.jsdelivr.net/npm/[email protected]/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.

八、Element-UI按需引入

  1. 安裝vite-plugin-style-import外掛
# 注意本外掛必須採用1.4.1版本,不能採用最新版2.0.0
pnpm add [email protected]^1.4.1 -D
  1. 增加vite.conf.js配置

    plugins: [
        // ...
        styleImport({
            libs: [
              {
                libraryName: 'element-ui',
                // styleLibraryName: 'theme-chalk',
                esModule: true,
                resolveStyle: (name) => {
                  return `theme-chalk/${name}.css`
                }
              }
            ],
          }),
        // ...
    ]

九、新增代理

  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/'))
              },
           },
           {
             // this is required for the SCSS modules
             find: /^~(.*)$/,
             replacement: '$1'
           }
         ]
       },
    )
}

十五、靜態檔案引入

  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)

    
    ### 十六、雪碧圖載入svg-sprite-loader替換
  3. 安裝vite-plugin-svg-icons外掛

    pnpm add vite-plugin-svg-icons -D
  4. 配置vite.config.js

    import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
    
    defineConfig({
     plugins: [
         // ...
         // 雪碧圖
     createSvgIconsPlugin({
         // 指定需要快取的圖示資料夾
         iconDirs: [path.resolve(__dirname, './src/icons/svg')],
         // 指定symbolId格式
         symbolId: 'icon-[dir]-[name]',
    
         /**
          * 自定義插入位置
          * @default: body-last
          */
         inject: 'body-last' | 'body-first',
    
         /**
          * custom dom id
          * @default: __svg__icons__dom__
          */
         customDomId: '__svg__icons__dom__'
       })
     ]
    })
  5. 在main.js中新增