写点什么

拥抱下一代前端工具链 -Vue 老项目迁移 Vite 探索

  • 2023-02-28
    北京
  • 本文字数:3392 字

    阅读完需:约 11 分钟

拥抱下一代前端工具链-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 顶部引入


// polyfillsif (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%。虽然前期遇到了许多的坑,但是成功后的感受就是一个字,"真香"。

发布于: 刚刚阅读数: 7
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
拥抱下一代前端工具链-Vue老项目迁移Vite探索_前端_京东科技开发者_InfoQ写作社区