乐趣区

关于vue.js:拥抱下一代前端工具链Vue老项目迁移Vite探索

作者:京东物流 邓道远

背景形容

随着我的项目的一直保护,代码越来越多,我的项目越来越大。调试代码的过程就变得极其苦楚,期待我的项目启动的工夫也越来越长,尤其是须要解决紧急问题的时候,切换我的项目启动,期待的工夫就会显得尤为的漫长。无法忍受这种开发效率的我,决定将老我的项目迁徙至 vite。

间隔 Vite 工具公布到当初曾经有了一些日子了,工具链与生态曾经趋于稳定,最新版本曾经更新到了 3.0,既然念头已起,心动不如口头。

1、什么是 Vite

vite 发音为 /vit/ 法语中就是快的意思,“人”如其名,就是快

  • 一个开发服务器,它基于原生 ES 模块,提供了丰盛的内建性能,如速度快到惊人的模块热更新(HRM)
  • 一套构建指令,它应用 rollop 来打包你的代码,并且是预配置的,可输入用于生产环境的高度优化过的动态资源。

2、为什么快

家喻户晓,当冷启动服务器时,基于打包器的启动必须优先抓取并构建你的整个利用,而后能力提供服务,这一抓取构建的过程随着文件越来越多,工夫也会越来越长。

而 Vite 却通过将利用中的木块辨别为 依赖 和源码两类,从而优化了大量的服务器启动工夫。

  • 依赖 大多为在开发时不会变动的纯 JavaScript。一些较大的依赖(例如有上百个模块的组件库)解决的代价也很高。依赖也通常会存在多种模块化格局(例如 ESM 或者 CommonJS)。
  • Vite 将会应用 esbuild 预构建依赖。esbuild 应用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。
  • 源码 通常蕴含一些并非间接是 JavaScript 的文件,须要转换(例如 JSX,CSS 或者 Vue/Svelte 组件),时常会被编辑。同时,并不是所有的源码都须要同时被加载(例如基于路由拆分的代码模块)。
  • Vite 以原生 ESM 形式提供源码。这实际上是让浏览器接管了打包程序的局部工作:Vite 只须要在浏览器申请源码时进行转换并按需提供源码。依据情景动静导入代码,即只在以后屏幕上理论应用时才会被解决。

3、如何实现老我的项目迁徙

以后我的项目是 Vue2.0,vue-cli4.0,node v14.18.2

3.1 首先咱们须要先明确我的项目构造

与原来的 Vue 老我的项目相比,模板文件 index.html 须要从 public 挪到我的项目根目录中,Vite 将 index.html 视为源码和模块图的一部分。因为咱们只有一个入口文件,所以在 index.html 中须要引入 main.ts

<script type="module" src="/src/main.ts"></script>

而且运行过程中可能会遇到上面写法引发的报错

<link rel="icon" href="<%= BASE_URL %>favicon.ico" />


[vite] Internal server error: URI malforme

解决办法是能够写一个简略的插件替换一下

res = code.replace(/<%=\s+BASE_URL\s+%>/g, baseDir);

与 Vue-cli 雷同,须要一个配置文件 vite.cofnig.js, 与原来的 vue.config.js 同级

3.2 装置依赖

既然咱们应用 Vite,那么咱们须要装置一个 vite 依赖。然而咱们的老我的项目是 Vue2.0,vite 优先反对 Vue3.0,所以咱们还须要一个转换工具 “vite-plugin-vue2”

npm i vite vite-plugin-vue2 -S

3.3 批改配置文件

批改 package.json 中的 scripts,启动和打包形式应用 vite

  • “serve”: “vite”,
  • “build”: “vite build”,

批改 vite.config.js,与 vue.config.js 类似

import {defineConfig} from 'vite'
import {createVuePlugin} from 'vite-plugin-vue2'
  
// https://vitejs.dev/config/   这一行能够减少编辑器代码提醒
export default defineConfig({
     plugins: [
       createVuePlugin({
 jsx: true, // 兼容我的项目中的 jsx 组件
      vueTemplateOptions: {}}),
     ],
     resolve: {extensions: ['.vue', '.js', '.ts', '.jsx', '.tsx', '.json'],
       alias: [
{
        find: '@',
        replacement: '/src'
}
]
     },
server: {
    open: true, // 控制台间接关上浏览器
    host: 'xxxx.jd.com', // 本地 host
    allowedHosts: ['.jd.com', '.jdwl.com', '.jd.co.th', '.jd.id'],
    port: 80,
    cors: true,
    proxy: {
      '/api': {
        target: 'https://xxx.jd.com',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '/api')
      }
    }
  },
 })

3.4 剔除原来的 webpack 相干依赖

能够手动剔除 也能够重新启动一个 vite 我的项目再将所需代码移动到 vite 我的项目中

3.5 启动利用

这个时候咱们就能够启动利用了,不出意外的话,会有许多的报错信息,不过不要慌,咱们一个一个的解决

4、遇见的问题汇总

4.1 环境变量

webpackl 里的环境变量是默认存储在 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 相同)。

当然,既然是老我的项目,这种调用地位会有很多,咱们能够应用比较简单的做法来兼容

export default defineConfig({
  define: {'process.env': {}
  },
})

4.2 global 变量

因为 VIte 是 ESM 机制,有些包外部应用了 node 的 global 对象,解决此问题能够通过自建 pollfill, 而后在 main.ts 顶部引入

// polyfills
if (typeof (window as any).global === 'undefined') {;(window as any).global = window
}

4.3 Scss 全局变量报错

这一点是 vite 与 vue-cli 配置形式不同引发,而且如果应用了环境变量也须要适配 vite 的写法兼容

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {additionalData: '$ossHostVariable: \'import.meta\u200b.env.VUE_APP_OSS_HOST\';'}
    }
  }
})

4.4 path 报错

Vite 是 ESM 机制 path 是 node 的包,所以须要兼容浏览器的引入形式,须要装置依赖“path-broswserfiy”

只须要将引入的包替换即可

import path from 'path' 
// 替换成
import path from 'path-broswserfiy' 

4.5 Require 报错

问题的引发与下面统一 都是模块加载形式的不同导致的,能够通过 ”
vite-plugin-require-transform” 插件来解决

import requireTransform from 'vite-plugin-require-transform'
export default defineConfig({
  plugins: [requireTransform({})
]
})

4.6 vue 组件的动静导入

vue 的组件导入形式有很多,vite 能够反对 () => import(‘/.vue’)的形式导入,不过与 webpack 的区别在于须要补全文件的后缀,动静导入须要 import.meta.glob 的形式

const load = import.meta.glob('@/views/**/index.vue');


export const constantRoutes: any = [
  {
    path: '/404',
    component: load['404']
  },
]

4.7 编译时的分包策略

const SPLIT_CHUNK_CONFIG = [
  {match: /[\\/]src[\\/]_?common(.*)/,
    output: 'chunk-common',
  },
  {match: /[\\/]src[\\/]_?component(.*)/,
    output: 'chunk-component',
  },
];


const rollupOptions = {
  output: {chunkFileNames: 'assets/js/[name]-[hash].js',
    entryFileNames: 'assets/js/[name]-[hash].js',
    assetFileNames: 'assets/static/[name]-[hash].[ext]',
    manualChunks(id) {for (const item of SPLIT_CHUNK_CONFIG) {const { match, output} = item;


        if (match.test(id)) {return output;}
      }


      if (id.includes('node_modules')) {return id.toString().split('node_modules/')[1].split('/')[0].toString();}
    },
  },
}

5、启动工夫

不多说了 上图

不过还会有一些问题,开发模式下比方页面首次加载工夫比拟迟缓,大概在 5s 左右,不过这也是能够了解的,毕竟编译过程都交给了浏览器,相比于老我的项目冷启动动辄 2 3 分钟的体验,曾经是天大的晋升了。

6、总结

最初再来回顾一下,整体的迁徙过程。

首先,明确我的项目构造,index.html 模板文件 提到根目录下,统计减少 vite.config.js 文件。

而后,编写配置文件 vite.config.js 留神与 vue.config.js 上的语法区别,留神兼容写法。

最初,解决我的项目中两种打包工具的不兼容写法。大部分还是模块标准的区别,node 环境的变量以及语法所引发,能够通过各种各样的插件来兼容解决。

以上即为本次迁徙的全副过程,丰盛、优化了前端工具链的构建流程,极大的晋升了开发人员的幸福感,以及开发体验,我的项目冷启动工夫更是晋升了百分之 99%。尽管后期遇到了许多的坑,然而胜利后的感触就是一个字,” 真香 ”。

退出移动版