艺灵设计

全部文章
×

记一次成功把Vue2后台项目改造成Vite2的踩坑经历

作者:艺灵设计 - 来源:http://www.yilingsj.com - 发布时间:2022-05-15 22:17:28 - 阅: - 评:0 - 积分:0

摘要:公司后台项目基于@花裤衩大神的vue-element-admin,随着项目功能的不断完善和需求不断增加,项目启动也变的越来越慢。慢到什么程度呢?我i7-10代16G内存的本本跑项目最快也需要近3分钟,实在难以忍受。几经折腾,终于成功改造成了vite。现在跑项目,5秒内即可跑起来,简直不要太爽!此次改造的过程遇到很多问题,现将遇到的问题做下记录,以防后续改造类似项目时继续踩坑。

一、项目背景

本次要改造的是一个基于vue-element-admin的后台项目。由于项目比较老,所以没有使用到typescript,用到的element-uivue都还停留在2版本上。最终改造完毕后依然是跟vue3没有半毛钱关系,只是引入了vite从而大大提升编译速度。

1.1、为什么要选择Vite

其实,在选择Vite之前,我尝试过使用esbuild对项目进行优化,但优化结果并不理想。主要原因还是我太菜了[:无奈]。无奈之下,我开始尝试使用vite来优化项目。断断续续折腾了好几次,终于成功的把项目跑起来了。那速度真叫一个快,真是没有对比就没有伤害。如下图: 原项目启动时长和vite版的对比

顺便提一嘴,如果看官想测试下项目运行所耗费的时间,可以安装一个speed-measure-webpack-plugin插件。

// 安装speed-measure-webpack-plugin
npm install speed-measure-webpack-plugin --save-dev

安装完后,修改下vue.config.js配置文件。

// 修改vue.config.js
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin') // 显示编译时长
module.exports = {
  ......// 此处省略若干行代码
  configureWebpack: {
    ......// 此处省略若干行代码
    plugins: [
      ......// 此处省略若干行代码
      new SpeedMeasurePlugin(), // 此行为新增
    ],
  },
}

现在,用npm run dev命令启动项目,在项目启动成功时,终端窗口中会打印编译时长。

二、迁移前的准备

接下来分享下自己一路走来踩过的坑,我的经验可能不能完全适用于看官的项目,但有很多地方还是通用的。

2.1、补全.vue后缀

由于vite无法识别忽略扩展名的.vue文件,(参见尤大大的回复→→→import *.vue file without .vue will not work,所以我的首要工作是必须把项目中所有有导入且省略.vue文件的地方给补全后缀。虽然可以在配置resolve.extensions中加入.vue来达到目的地,但官方文档还是不建议。补全.vue后缀的这个工程量还是比较大的,共修改了快200个文件。

2.2、移动public/index.html的位置

vue2项目中的index.html文件是在根目录下的public目录中,但vite建议把index.html直接放到根目录下。相关说明→→→index.html 与项目根目录

如果项目的页面标题是唯一且不变的,可以直接在title中写死。除此之外,还需要导入入口文件main.js

// 修改index.html
<!-- 需要修改的地方 -->
<link rel="icon" href="/favicon.ico">
<title>网页标题</title>

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

2.2.1、通过vite-plugin-html插件来修改页面标题

除了上面的修改方式外,还可以通过安装vite-plugin-html插件来解决。

// 安装vite-plugin-html
npm install vite-plugin-html --save-dev
// 修改vite.config.js
...... // 此处省略若干行代码
import html from 'vite-plugin-html' // 为index.html提供minify和基于EJS模板功能
export default ({ mode }) => {
  return defineConfig({
    ...... // 此处省略若干行代码
    plugins: [
      ...... // 此处省略若干行代码
      /* 新增代码开始↓↓↓ */
      html({
        inject: {
          injectData: {
            title: 'xxx后台管理系统',
          },
        },
      })
      /* 新增代码结束↑↑↑ */
    ],
  })
}

上面两种方式都能修改index.html文件的标题和内容,还有一些别的插件也能做到,我还没摸索过,就不写献丑了。

2.3、新建vite.config.js文件

在vue2中,项目根目录中会有一个vue.config.js的文件,现在我们要改造成vite,也得创建对应的配置文件。官方文档:→→→配置 Vite。看完官方文档后,我把vue.config.js复制了一份,并改名为vite.config.js。然后按官方的文档进行了一些简单的修改,代码如下:

// vite.config.js配置文件
/* 老代码开始↓↓↓ */
import path from 'path' // 老代码
function resolve(dir) {
  return path.join(__dirname, dir)
}
...... // 此处省略若干行代码
/* 老代码结束↑↑↑ */
import { defineConfig } from 'vite' // 此行是新增
export default ({ mode }) => { // 此行是新增
  return defineConfig({ // 此行是新增
    ...... // 老vue.config.js配置内容,实际上那些代码在这里是没有用的,需要按vite的规范来写
    /* 新代码开始↓↓↓ */
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: `@import "/src/styles/theme.scss";`, // 挂载全局皮肤样式
        }
      }
    }
    /* 新代码结束↑↑↑ */
  })
}

从上面代码可以看出,我在一开始的时候想延续vue.config.js中的配置代码,但项目启动的时候却出现了一堆错误。而这些错误也是决定项目能否迁移成功的关键。如果看官也想改造老项目,不建议把原vue.config.js内的配置保留,应该换成对应的vite版。

2.3.1、局部安装vite

正如上面代码中第9行所写import { defineConfig } from 'vite',当前环境是没有安装vite的,所以第一步就是安装vite。我在项目根目录下,右键并选中Git Bash Here,此时打开了命令行窗口,也可以使用cmd进入项目目录。由于是在当前项目安装,所以我没有带-g

// 局部安装vite
npm install vite --save-dev // 在当前项目中安装vite

2.4、安装vite-plugin-vue2

这个插件是vite官方曾提到的,因为我的项目是基于vue2的,所以就装一个吧。切换到git窗口,然后输入下方命令并回车,等待安装。

// 安装vite-plugin-vue2
npm install vite-plugin-vue2 --save-dev

安装完后要继续修改vite.config.js配置文件,修改见下方。

// vite.config.js配置文件
...... // 此处省略若干行代码
import { createVuePlugin } from 'vite-plugin-vue2' // Vue2支持,https://vitejs.cn/guide/features.html#vue
export default ({ mode }) => {
  return defineConfig({
    ...... // 老vue.config.js配置内容,实际上那些代码在这里是没有用的,需要按vite的规范来写
    /* 新代码开始↓↓↓ */
    plugins: [
      createVuePlugin()
    ],
    /* 新代码结束↑↑↑ */
    ...... // 这里是上面提到的css配置
  })
}

2.5、修改package.json,添加启动命令

接着打开package.json文件,做简单的修改。

// package.json未修改前
{
  ...... // 原代码
  "scripts": {
    // "dev": "vue-cli-service serve", // 修改前
    "dev": "vite", // 修改后,也就是把原来的vue-cli-service serve换成了 vite 即可
    ...... // 省略若干行
  },
  "devDependencies": {
    /* 删除以@vue/cli-plugin开头的库↓↓↓ */
    "@vue/cli-plugin-babel": "4.4.4",
    "@vue/cli-plugin-unit-jest": "4.4.4",
    "@vue/cli-plugin-eslint": "4.4.4",
    /* 删除以"@vue/cli-plugin开头的库↑↑↑ */
    ...... // 省略若干行
  }
}

三、坎坷的改造之路即将开始,报错接二连三!

有必要说一下,由于项目中安装插件的完整程度和使用三方库不同的原因,下方的报错可能会存在顺序问题,也可能在看官的项目中无法复现。所以请不要较真,针对自己项目做优化就好了哈。

3.1、Failed to resolve import "@/assets/js/china.json"

前面的修改还是很简单的,接下来我便开心的切到了git窗口,输入npm run dev并回车来启动项目。还没等我反应过来,终端窗口中已显示 ready in 3460ms. 。卧槽,这也太快了吧!我带着不可思议的心情打开了浏览器,结果有报错信息存在。

// echarts有报错
[plugin:vite:import-analysis] Failed to resolve import "@/assets/js/china.json" from "src\main.js". Does the file exist?
D:/2021/work/v5_element/src/main.js:17:36
15 |  require('echarts/lib/component/title')
16 |
17 |  import china from '@/assets/js/china.json' // 引入json文件
   |                                     ^
18 |  echarts.registerMap('china', china) // 在echarts中注册使用这个文件

当时选择的是先注释掉代码,等项目跑起来了再找解决方案。友情提醒:解决方案见下面的3.11

3.2、Failed to resolve import "styles/index.scss"

注释掉代码后,浏览器上显示新的报错信息。

// 新的报错
[plugin:vite:import-analysis] Failed to resolve import "~@/styles/index.scss" from "src\main.js". Does the file exist?
D:/2021/work/v5_element/src/main.js:20:28
18 |  // echarts.registerMap('china', china) // 在echarts中注册使用这个文件
19 |
20 |  import '~@/styles/index.scss' // global css
   |                             ^
21 |  import '~@/styles/form-tool.scss' // 万能表单

我去,咋又报css文件的错误了?不应该啊!没换成vite前一切正常,而且我也把原vue.config.js中的所有配置都保留了下来。我是选择继续注释还是选择解决问题呢?选择注释只能暂时“解决问题”,可问题终究还是要解决。于是我又打开了vite的官网,找到了配置别名的resolve.alias,继续修改vite.config.js

// 在vite.config.js中添加alias别名
...... // 此处省略若干行代码
export default ({ mode }) => {
  return defineConfig({
    ...... // 此处省略若干行代码
    /* 配置别名开始↓↓↓ */
    resolve: {
      alias: {
        '@': resolve('src'), // 用@代替src目录
        assets: resolve('src/assets'), // 用assets代替assets目录,可根据自己情况进行修改
        styles: resolve('src/styles'), // 用styles代替css样式目录,可根据自己情况进行修改
      },
    },
    /* 配置别名结束↑↑↑ */
  })
}

3.3、Can't find stylesheet to import.

现在我把刚注释掉的china.json又开启了,再看浏览器,3.1的报错已经没有了,但又有新的css报错。

// 新的报错
[plugin:vite:css] Can't find stylesheet to import.
    ╷
150 │ @import '~@/styles/mixin.scss';
    │         ^^^^^^^^^^^^^^^^^^^^^^
    ╵
  src\layout\index.vue 150:9  root stylesheet
D:/2021/work/v5_element/D:/2021/work/v5_element/src/layout/index.vue:150:0

哦,原来是项目中还有一些老代码需要修改,然后我对引入css的一些文件进行了对应的修改。保存后再看浏览器,没有报错了。实际上是假象哈~。此时页面一片空白,不应该啊!

3.4、The requested module '/src/settings.js' does not provide an export named 'default'

凭直觉我熟练的打开了控制台,发现有个红×1的存在,果然还真有报错。让我看看这回报的是什么错。

// 新的报错
Uncaught SyntaxError: The requested module '/src/settings.js' does not provide an export named 'default'

这回就尴尬了,在网上搜索了一圈,最后发现说是要装@originjs/vite-plugin-commonjs。于是,我报着试一试的心态安装了npm包。

// 安装@originjs/vite-plugin-commonjs
npm install @originjs/vite-plugin-commonjs --save-dev

安装完后需要修改vite.config.js

// 让浏览器支持commonjs语法
...... // 此处省略若干行代码
import { viteCommonjs } from '@originjs/vite-plugin-commonjs' // 让浏览器支持commonjs语法
export default ({ mode }) => {
  return defineConfig({
    plugins: [
      ...... // 此处省略若干行代码
      viteCommonjs() // 此行为新增
    ],
    ...... // 此处省略若干行代码
  })
}

3.5、Cannot find module '@vue/cli-plugin-babel/preset'

现在再看浏览器,刚才的报错已经没有了,但又出现了新的错误。

// 新的报错
[plugin:vite-plugin-vue2] Cannot find module '@vue/cli-plugin-babel/preset'
Require stack:
- D:\2021\work\v5_element\node_modules\@babel\core\lib\config\files\plugins.js
- D:\2021\work\v5_element\node_modules\@babel\core\lib\config\files\index.js
- D:\2021\work\v5_element\node_modules\@babel\core\lib\index.js
- D:\2021\work\v5_element\node_modules\vue-template-babel-compiler\lib\index.js
- D:\2021\work\v5_element\node_modules\vite-plugin-vue2\dist\template\compileTemplate.js
- D:\2021\work\v5_element\node_modules\vite-plugin-vue2\dist\template.js
- D:\2021\work\v5_element\node_modules\vite-plugin-vue2\dist\index.js
- D:\2021\work\v5_element\vite.config.js
- D:\2021\work\v5_element\node_modules\vite\dist\node\chunks\dep-e1fc1d62.js
- D:\2021\work\v5_element\node_modules\vite\dist\node\cli.js
- D:\2021\work\v5_element\node_modules\vite\bin\vite.js
D:/2021/work/v5_element/src/App.vue

3.6、Cannot find module '@babel/preset-env'

我在vue2上已经安装过了'@vue/cli-plugin-babel/preset',为啥还提示呢?莫非与现在的vite不兼容?我尝试着注释掉了babel.config.js中的这行代码,此时项目有了新的报错。

// 新的报错
[plugin:vite-plugin-vue2] Cannot find module '@babel/preset-env'
Require stack:
- D:\2021\work\v5_element\node_modules\@babel\core\lib\config\files\plugins.js
- D:\2021\work\v5_element\node_modules\@babel\core\lib\config\files\index.js
- D:\2021\work\v5_element\node_modules\@babel\core\lib\index.js
- D:\2021\work\v5_element\node_modules\vue-template-babel-compiler\lib\index.js
- D:\2021\work\v5_element\node_modules\vite-plugin-vue2\dist\template\compileTemplate.js
- D:\2021\work\v5_element\node_modules\vite-plugin-vue2\dist\template.js
- D:\2021\work\v5_element\node_modules\vite-plugin-vue2\dist\index.js
- D:\2021\work\v5_element\vite.config.js
- D:\2021\work\v5_element\node_modules\vite\dist\node\chunks\dep-e1fc1d62.js
- D:\2021\work\v5_element\node_modules\vite\dist\node\cli.js
- D:\2021\work\v5_element\node_modules\vite\bin\vite.js
D:/2021/work/v5_element/src/App.vue

然后你们懂的,继续安装@babel/preset-env并做相关配置。

// 安装@babel/preset-env
npm install @babel/preset-env --save-dev
// 修改babel.config.js
...... // 省略一些配置
module.exports = {
  presets: [
    // '@vue/cli-plugin-babel/preset', // 这个是vue2上的
    '@babel/preset-env', // vite版需要安装这个
  ],
  ...... // 省略一些配置
}

3.7、Failed to resolve import "canvas" from ...

再刷新浏览器,又报了别的错误。

// 新的报错
[plugin:vite:import-analysis] Failed to resolve import "canvas" from "node_modules\.vite\deps\mockjs.js?v=2024cff1". Does the file exist?
D:/2021/work/v5_element/node_modules/.vite/deps/mockjs.js:1:45
1  |  import * as __require_for_vite_rWAjLt from "canvas";
   |                                              ^
2  |  import {
3  |    __commonJS

网上说是未将Commonjs转成ESM,需要安装cjs2esmodule包。

// 安装cjs2esmodule
npm install cjs2esmodule --save-dev

安装完后需要修改vite.config.js

// 导入cjs2esmodule
...... // 此处省略若干行代码
import { cjs2esmVitePlugin } from 'cjs2esmodule' // 将 commonjs 转化为 es module
export default ({ mode }) => {
  return defineConfig({
    plugins: [
      ...... // 此处省略若干行代码
      cjs2esmVitePlugin() // 此行为新增
    ],
    ...... // 此处省略若干行代码
  })
}

3.8、Cannot overwrite a zero-length range – use appendLeft ...

现在再看浏览器,仍然是报新的错误。

// 新的报错
[plugin:vite:import-analysis] Cannot overwrite a zero-length range – use appendLeft or prependRight instead
D:/2021/work/v5_element/src/layout/components/Navbar.vue

我又打开Navbar.vue这个文件,找了好久才发现原来是因为一处img没有设置src引起,这报错真坑啊!如下图:一个空的img标签引起报错.png 一个空的img标签引起报错

3.9、process is not defined

注释掉这个空img标签后,又有新的报错了。

// process is not defined
Uncaught ReferenceError: process is not defined
    at request.js?t=1652104992810:15:5

点开报错信息后发现是process.env环境变量引起。

// process is not defined
const {
  VUE_APP_PREVIEW_PICTURE,
  VUE_APP_KEFU,
  VUE_APP_HELP,
  VUE_APP_FINISH,
  NODE_ENV,
  VUE_APP_ENV,
  VUE_APP_DOMAIN,
  VUE_APP_BASE_API,
} = process.env

翻看vite官网,发现用import.meta.env替代了process,官方文档:→→→环境变量。除此之外,自定义的环境变量由原来的VUE_更换成了VITE_。由于这个项目有4个环境,分别是devtestpreprod,所以我需要把这4个项目中的环境变量前缀修改掉,并且把用到process.env的地方给更新掉。

下面一步步进行修改,第一步先修改环境变量文件。

修改之前↓↓↓

// 未修改前的.env.dev文件
# 自定义环境标识,装修预览使用↓↓↓
VUE_APP_ENV = 'development'

# 线上域名↓↓↓
VUE_APP_DOMAIN = 'xxx'

# api接口域名目录↓↓↓
VUE_APP_BASE_API = 'home'

# 上传图片后的预览地址↓↓↓
VUE_APP_PREVIEW_PICTURE = 'xxx'

# 生成目录地址↓↓↓
VUE_APP_OUTPUTDIR = 'dist/dev'

# 装修地址↓↓↓
VUE_APP_FINISH = 'xxx'

修改之后↓↓↓

// .env.dev文件
# 自定义环境标识,装修预览使用↓↓↓
VITE_APP_ENV = 'development'

# 线上域名↓↓↓
VITE_APP_DOMAIN = 'xxx'

# api接口域名目录↓↓↓
VITE_APP_BASE_API = 'home'

# 上传图片后的预览地址↓↓↓
VITE_APP_PREVIEW_PICTURE = 'xxx'

# 生成目录地址↓↓↓
VITE_APP_OUTPUTDIR = 'dist/dev'

# 装修地址↓↓↓
VITE_APP_FINISH = 'xxx'

3.10、require is not defined

其他几个文件也是同样的修改,修改后我把原来的process.env全换成了import.meta.env。现在没有报环境变量的错了,其实在后面会再报环境变量的错。接下来报的是一个require的错。

// require is not defined
Uncaught ReferenceError: require is not defined
    at index.js:7:13

点开报错信息,定位了报错代码如下。

// require.context报错
const req = require.context('./svg', false, /\.svg$/)

网上百度了下,说是要用import.meta.globEager来进行导入。

// 修改后的代码
// const req = require.context('./svg', false, /\.svg$/) // 这是原来的代码
const req = import.meta.globEager('./svg') // 这是修改后的代码

3.11、Uncaught TypeError: echarts.registerMap is not a function

保存后,继续有报错。

// 新的报错
Uncaught TypeError: echarts.registerMap is not a function
    at main.js?t=1652148127482:22:9

定位代码如下:

// 定位错误
// 引入基本模板
const echarts = require('echarts/lib/echarts')
// 引入柱状图组件
require('echarts/lib/chart/bar')
// 引入提示框和title组件
require('echarts/lib/component/tooltip')
require('echarts/lib/component/title')
import china from '@/assets/js/china.json' // 引入json文件
echarts.registerMap('china', china) // 在echarts中注册使用这个文件

这就尴尬了,我在网上搜索了一圈,没有搜索到直接的答案。然后有网友说用import * as echarts的方式来导入echarts。我试了下,暂时是不报错了。

// 用import代替require
// const echarts = require('echarts/lib/echarts') // 未修改前会导致报错
import * as echarts from 'echarts/lib/echarts' // 修改后就不报错了
...... // 其他代码无任何修改

3.12、require is not defined

虽然echarts暂时不报错了,但控制台还是有错误。

// require is not defined
Uncaught ReferenceError: require is not defined
    at list.js:98:5
    at vue-router.esm.js:1790:17
    at vue-router.esm.js:1817:66
    at Array.map (<anonymous>)
    at vue-router.esm.js:1817:38
    at Array.map (<anonymous>)
    at flatMapComponents (vue-router.esm.js:1816:26)
    at vue-router.esm.js:1752:5
    at iterator (vue-router.esm.js:1959:7)
    at step (vue-router.esm.js:1733:9)

点开一看,原来是动态拼接路由文件路径这里有报错。

// 动态拼接路由文件路径
/**
 * @author: xuzongwei (1083467001@qq.com)
 * @description: 拼接组件路径
 * @param {String} path 路由路径
 * @return {*}
 * @Date: 2021-09-22 08:36:26
 */
function getViews(path) {
  return resolve => {
    require.ensure([], require => {
      try {
        resolve(require('@/views/' + path + '.vue'));
      } catch {
        resolve(__require_for_vite_IFY10w.default || __require_for_vite_IFY10w);
      }
    });
  };
}

这个报错在前面也出现过,继续用vite官方中提供的全局导入方法import.meta.glob即可。

// vite版动态拼接路由文件路径
function getViews(path) {
  const modules = import.meta.glob('../views/**/*.vue') // 路由文件路径(vite版)
  try {
    return modules['../views/' + path + '.vue']
  } catch {
    return modules['../views/404.vue']
  }
}

此时,浏览器显示了熟悉的后台登录界面,输入帐号密码后成功进入后台,好激动啊!!!如图:后台终于跑起来了

3.13、Module "path" has been externalized for browser

虽然项目是跑起来了,但控制台中还是有报错。

// 控制台中有报错
Error: Module "path" has been externalized for browser compatibility and cannot be accessed in client code.
    at Object.get (__vite-browser-external:path:3:11)
    at VueComponent.resolvePath (SidebarItem.vue:126:1)
    at Proxy.render (SidebarItem.vue?vue&type=template&lang.js:5:63)
    at VueComponent.Vue2._render (vue.runtime.esm.js:3542:22)
    at VueComponent.updateComponent (vue.runtime.esm.js:4060:21)
    at Watcher2.get (vue.runtime.esm.js:4473:25)
    at Watcher2.run (vue.runtime.esm.js:4548:22)
    at flushSchedulerQueue (vue.runtime.esm.js:4304:13)
    at Array.<anonymous> (vue.runtime.esm.js:1980:12)
    at flushCallbacks (vue.runtime.esm.js:1906:12)

继续点开报错信息进行定位分析,发现是SidebarItem.vue文件。原来是path.resolve的问题,这回我是真的没招了。再加上现有的侧边导航本身就需要优化,我决定再花些时间把导航重构下来解决这个问题。

// 定位报错信息
resolvePath(routePath) {
      if (isExternal(routePath)) {
        return routePath
      }
      if (isExternal(this.basePath)) {
        return this.basePath
      }
      return path.resolve(this.basePath, routePath) // 报错定位到这一行
    },

我又花了几个小时来重构侧边导航,当我换上新的导航组件后,控制台中终于没有报错了!耶!耶!耶!

特别强调一下:项目能跑起来并且当前页面无报错,并不意味着就没有错误了。因为有些页面在进入后或需要手动调用一些功能时,可能会继续触发报错。特别是打包过程及打包后预览的时候,报错将会继续上演!所以,建议各位看官在改完后,把页面多点点,尽可能发现更多的问题,避免打包后再去解决一些错误。

接下来是我认为在所有报错信息中比较坑的一个!为什么这样说呢?因为报错原因真的很费解!

3.14、This experimental syntax requires enabling

在项目中,跳转到某个页面后,浏览器报错了。

// 继续报错
[plugin:cjs2esmVitePlugin] This experimental syntax requires enabling one of the following parser plugin(s): "jsx", "flow", "typescript". (2:0)
D:/2021/work/v5_element/src/views/platform/administration/qywx/index.vue:2:0
1  |
   |   ^
2  |  <div class="bg">
3  |    <div class="main">

网上搜索报错信息,没有找到对应的解决方法。有些文章说道需要安装对应的插件,例如:@babel/eslint-parser@typescript-eslint。可问题是:这个vue2的后台项目一没使用ts、二没使用jsx,为啥要通过装插件来解决问题呢?

为了找到问题根本原因,我进入了对应的页面,然后把有嫌疑的地方一处处注释掉。就这样,我终于找到了问题所在,你们可能也不会想到引起报错的原因竟然还是跟图片有关!

// 引起报错的代码
<img :src="require('@/assets/images/img/tu-' + id + '.png')" alt="" />

没错!就是上面这行代码引起的。于是我按照vite官网说的静态资源引入方式,进行了如下改造。

// 使用new URL导入静态资源
<img :src="getImageUrl(id)" alt="" />
// methods中定义方法
getImageUrl(id) {
  return new URL('/src/assets/images/img/tu-' + id + '.png', import.meta.url).href
},

现在再刷新相关页面,没有报错了。点击示例按钮,图片也能动态显示了。但这里会留下一个潜在的坑,因为在打包后,浏览器可能会因不识别import.meta.url而导致报错!

// 报错如下
TypeError: Failed to construct 'URL': Invalid URL
    at d.getImageUrl (index.40010f94.js:1:4769)
    at Proxy.o (index.40010f94.js:1:3351)
    at d.WY.e._render (index.8cd628bf.js:13:1690)
    at d.i (index.8cd628bf.js:14:4077)
    at cl.get (index.8cd628bf.js:14:7327)
    at new cl (index.8cd628bf.js:14:7249)
    at t9 (index.8cd628bf.js:14:4091)
    at d.qt.$mount (index.8cd628bf.js:15:37567)
    at init (index.8cd628bf.js:12:21362)
    at r (index.8cd628bf.js:12:22751)

而这个报错,正好是我们刚使用了getImageUrl引起。经过调试发现,打包后的import.meta.url变成了C.url,其值为undefined。如下图:import.meta.url在打包后变成了undefined

另外再补充一点儿,如果看官没有使用new URL而是使用绝对路径的方案,也有可能会出现开发时正常打包后报错的情况发生。其主要原因还是因为打包后的资源路径与开发时的路径不同导致。示例如下:

// 绝对路径的方案
<!-- 友情提示:虽然在开发阶段使用下方代码能正常显示图片,但打包后就会报错。因为资源路径有变化,具体可到dist/assets目录下查看 -->
<img :src="`/src/assets/images/img/tu-${id}.png`" alt="" />

如果非要使用new URL的方式来获取图片路径,我没有找到解决打包后路径异常的方案。经测试发现,以下三种方法都能解决打包后的图片路径异常的问题。

3.14.1、使用import的方式将资源引入为 URL

参见官网说明→→→ 将资源引入为 URL

// 将资源引入为 URL
<img :src="getImageUrl(id)" alt="" />

// js中直接导入图片
import imgUrlTu1 from '/src/assets/images/img/tu-1.png'
import imgUrlTu2 from '/src/assets/images/img/tu-2.png'
import imgUrlTu3 from '/src/assets/images/img/tu-3.png'
import imgUrlTu4 from '/src/assets/images/img/tu-4.png'

// 在data中定义
data(){
  return {
    imgUrlTu1,
    imgUrlTu2,
    imgUrlTu3,
    imgUrlTu4,
  }
}

// methods中定义方法
getImageUrl(id) {
  return this['imgUrlTu' + id]
},

如果觉得第一种使用import的方式比较麻烦,还可以通过v-if来实现。如果是单张图片还好,要是多张图片的话,效率也很低下,这里不做推荐。

// v-if条件判断
<template v-if="id === 1">
  <img src="@/assets/images/img/tu-1.png" alt="" />
</template>
<template v-else-if="id === 2">
  <img src="@/assets/images/img/tu-2.png" alt="" />
</template>
<template v-else-if="id === 3">
  <img src="@/assets/images/img/tu-3.png" alt="" />
</template>
<template v-else-if="id === 4">
  <img src="@/assets/images/img/tu-4.png" alt="" />
</template>

3.14.2、使用 import.meta.globEager 导入图片

// import.meta.globEager的方案
<img :src="getImageUrl(`img/tu-${id}.png`)" alt="" />

// js中导入所有图片
const modules = import.meta.globEager('/src/assets/images/**/*.*') // 参考链接→→→:https://www.136.la/ios/show-12071.html

// methods中定义方法
getImageUrl(id) {
  const path = `/src/assets/images/${name}`
  return modules[path].default
},

目前,我在项目中使用的是方法二。因为项目中图片有很多,如果每处都手动导入,则比较麻烦和费时,也不利于维护。另外还有一个原因就是,通过这种方式,不用担心打包后图片路径发生变化的问题。

3.14.3、把图片全放到public目录下

因为vite在打包时,不会对public目录下的文件做处理而是直接复制到了项目目录下。这样也能避免打包后图片路径找不到的问题。

3.15、开发时正常,打包后报require is not defined

如下图:开发时正常打包后后报require的错误

这就离谱啊!在开发阶段这里的require不报错,图片也能正常显示。打包后一跑项目,结果就报错了。关于解决方案的话,参考3.14中的第二种方法。

3.16、打包时检测css语法

虽然这个不是影响功能的报错,但这个错误在打包时竟然被检测到了!不得不说,这一点还是比较贴心的。这不,一同事因为手抖把font-size:写成了font-siz:,结果在打包时被检测到了。如下图:打包时检测到css样式有误

经过上面的不停折腾后,项目终于能成功的跑起来了,而且在打包后也能成功的跑起来了哦!

四、多环境修改

我们的项目有4个环境,开发阶段和打包的都要同步修改,实际上就是把vue-cli-service serve换成vite。如果看官的项目也支持多环境,别忘了改其他几个环境中的启动命令哈!

// 修改package.json以支持多环境
{
  ...... // 省略若干行代码
  "scripts": {
    "dev": "vite",
    /* vue2的启动命令↓↓↓ */
    // "dev:dev": "cross-env NODE_ENV=development vue-cli-service serve --mode dev",
    // "dev:test": "cross-env NODE_ENV=test vue-cli-service serve --mode test",
    // "dev:pre": "cross-env NODE_ENV=preshopv5 vue-cli-service serve --mode preshopv5",
    // "dev:prod": "cross-env NODE_ENV=production vue-cli-service serve --mode production",
    /* vue2的启动命令↑↑↑ */

    /* vite版的启动命令↓↓↓ */
    "dev:dev": "cross-env NODE_ENV=development vite --mode dev",
    "dev:test": "cross-env NODE_ENV=test vite --mode test",
    "dev:pre": "cross-env NODE_ENV=preshopv5 vite --mode preshopv5",
    "dev:prod": "cross-env NODE_ENV=production vite --mode production",
    /* vite版的启动命令↑↑↑ */

    /* vite版的打包命令↓↓↓ */
    "build:dev": "cross-env NODE_ENV=development vite --mode dev",
    "build:test": "cross-env NODE_ENV=test vite --mode test",
    "build:pre": "cross-env NODE_ENV=preshopv5 vite --mode preshopv5",
    "build:prod": "cross-env NODE_ENV=production vite --mode production",
    /* vite版的打包命令↑↑↑ */
    ...... // 省略若干行代码
  }
}

改完在终端窗口中输入对应的命令并回车,看看项目能否成功启动或成功打包。

// 多环境启动
npm run dev:test // 启动测试环境
npm run dev:pre // 启动预上线环境
npm run dev:prod // 启动正式环境

npm run build:dev // 打包测试环境
npm run build:test // 打包测试环境
npm run build:pre // 打包预上线环境
npm run build:prod // 打包正式环境

五、一些细节优化和暴露出来的问题

5.1、解放双手,自动打开浏览器

vue项目可以通过配置来解决手动打开浏览器的问题,现在的vite同样也是支持的。

// 配置server.open实现自动打开浏览器
...... // 此处省略若干行代码
export default ({ mode }) => {
  return defineConfig({
    server: {
      open: true, // 自动打开浏览器
    },
    ...... // 此处省略若干行代码
  })
}

现在,我们在终端中输入npm run dev,片刻后就能自动打开浏览器了哈。

5.2、大家好才是真的好!

目前来说,改造完的vite版项目在我自己电脑上已经没有问题了。于是我决定向组员推广,可谁知却翻了车!7个人中有3个人无法顺利把项目跑起来,一看报错信息,全长一样!

// 启动期间的报错:Unhandled 'error' event
events.js:292
  throw er; // Unhandled 'error' event
  ^
Error: spawn undefined\System32WindowsPowerShell\v1.0\powershell ENOENT
  at Process.ChildProcess._handle.onexit (internal/child_process.js:269:19)
  at onErrorNT (internal/child_process.js:465:16)
  at processTicksAndRejections (internal/process/task_queues.js:80:21)
Emitted 'error' event on ChildProcess instance at:
  at Process.ChildProcess._handle.onexit (internal/child_process.js:275:12)
  at onErrorNT (internal/child_process.js:465:16)
  at processTicksAndRejections (internal/process/task_queues.js:80:21) {
  errno: -4058,
  coee: 'ENOENT',
  syscall: 'spawn undefined\\System32\\WindowsPowerShe11\\v1.0\\powershell',
  path: 'undefined\\System32\\WindowsPowerShe11\\v1.0\\powershell',
  spawnargs:[
  '-NoProfile',
  '-Nonlnteractive',
  '- ExecutionPolicy',
  'Bypass',
  '-EncodedCoinmand',
  ......

这个报错很奇怪,path后面的powershell路径竟然是undefined!我在网上搜索了好久也没有找到解决方案。有尝试过把本地的node_modules打成压缩包后覆盖到另外两位同样的项目中,但并没有什么用。有尝试过设置powershell的环境变量为绝对路径,此时有一个同事能把项目跑起来了。当时我以为我找到方法了,然后又如法炮制的给剩下2个小伙伴设置同样的配置,结果并没有什么卵用。[:尴尬]

又过了好久,@牟万发来一张截图,意思是把5.1中的server.open的值改为false就不报错了。

如实说,我当时是质疑的,毕竟怎么看这个报错也跟server.open是没半毛钱关系的啊!然后我跑过去亲眼看着他操作,结果还真的不报错了!手动打开浏览器并粘贴http://localhost:3000到地址栏一回车,正常访问项目。另一位同事也按此方法操作,也成功访问项目。这......,好吧!手动打开浏览器就手动打开吧,毕竟也是能正常工作的哈!

5.3、打包后Element-UI字体图标库路径报错

这个问题现在复现不了了,在我前几次的改造过程中曾出现过,所以记录下来。但为啥现在又无法复现,我也有些摸不着头脑。

我在预览打包后的项目时,发现页面上的字体图标不正常。通过定位分析原来是Element-UI的字体图标库路径出现了问题。然后手动把base的值改为'/'后就正常了。

// 修改vite.config.js
...... // 此处省略若干行代码
export default ({ mode }) => {
  return defineConfig({
    /* 新增开始↓↓↓ */
    base: '/', // 开发或生产环境服务的公共基础路径 https://vitejs.cn/config/#base
    /* 新增结束↑↑↑ */
    ...... // 此处省略若干行代码
  })
}

5.4、安装@vitejs/plugin-legacy来做浏览器兼容处理

这个@vitejs/plugin-legacy是vite官网插件提到过的,其作用是:为打包后的文件提供传统浏览器兼容性支持。为了让我们的代码更加健壮,还是装一下吧。

// 安装@vitejs/plugin-legacy
npm install @vitejs/plugin-legacy --save-dev // 安装插件

安装后接着要修改vite.config.js配置文件,查看文档→→→@vitejs/plugin-legacy

// 修改vite.config.js文件
...... // 此处省略若干行代码
import legacy from '@vitejs/plugin-legacy' // 为打包后的文件提供传统浏览器兼容性支持
export default ({ mode }) => {
  return defineConfig({
    plugins: [
      ...... // 此处省略若干行代码
      /* 新增开始↓↓↓ */
      legacy({
        targets: ['defaults', 'ie >= 11', 'chrome 52'],
        additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
      }),
      /* 新增结束↑↑↑ */
    ],
    ...... // 此处省略若干行代码
  })
}

六、谈谈使用vite后的感受

目前来说,我们前端组员已使用vite超过2个星期了,大家普遍最直观的感受就是:快!!!那有没有用着不爽或需要优化的地方呢?当然是有的。个人总结有以下几点:

  1. 项目打包时间变长,现在的打包时间至少是之前的2倍以上。
  2. 打包后的资源文件默认全放在assets目录中,里面夹杂着.css.js.jpg.svg等文件,目录需要更细分化。
  3. 第三方库未按需加载,项目中用到的element-uilodashecharts全量打包了,增加了项目体积。怀疑打包慢跟这个也有关系。

鉴于上面提到的三点,我还在摸索中,等有结果了我再来一篇分享吧。

最后,感谢以下几篇文章对本文的帮助。

  1. Migrate From Vue-cli to Vite
  2. Migrate From Vue-cli to Vite
  3. vite + Vue3 遇到的一些问题汇总

转载声明:
  若亲想转载本文到其它平台,请务必保留本文出处!
本文链接:/xwzj/2022-05-15/Migrate-vue2-project-to-vite2.html

若亲不想直保留地址,含蓄保留也行。艺灵不想再看到有人拿我的技术文章到他的地盘或者是其它平台做教(装)程(B)而不留下我的痕迹。文章你可以随便转载,随便修改,但请尊重艺灵的劳动成果!谢谢理解。

亲,扫个码支持一下艺灵呗~
如果您觉得本文的内容对您有所帮助,您可以用支付宝打赏下艺灵哦!

Tag: 优化项目 vue2 vue-element-admin vite esbuild

上一篇: 记一次在优化Axios请求方式中遇到的坑团经历   下一篇: @vue/cli3项目开发之优化前端一键自动部署代码到服务器插件,支持上传到git仓库和备份解压

评论区