logo

Vue2.7升级踩坑记录

Thu Feb 02 2023 Posted last year

先放一个官方迁移文档地址

为什么要升级Vue2.7? #

  • 统一Vue2与Vue3的写法从而方便已有项目的迁移或重构
  • 方便抽离hooks从而获得更好的开发体验

升级步骤 #

一、vue-cli 相关的包 #

涉及范围:

"@vue/cli-plugin-babel": "~4.5.18",
"@vue/cli-plugin-eslint": "~4.5.18",
"@vue/cli-plugin-router": "~4.5.18",
"@vue/cli-plugin-unit-jest": "~4.5.18",
"@vue/cli-plugin-vuex": "~4.5.18",
"@vue/cli-service": "~4.5.18",

本来想尝试将脚手架升级到5.x,但无奈 webpack5 有很多 breaking changes, 常用的 loader 和 plugin 都有大版本变更,改起来实在有心无力,所以退而求次只升级到4.x的最新版本。

二、vue 相关的包 #

涉及范围:

"vue": "^2.7.13",
"vue-router": "~3.6.5",
"vuex": "~3.6.2",
// 2.7 版本不再需要 vue-template-compile,可以移除
--"vue-template-compiler": "^2.6.11",

除了要将 vue 的版本升至2.7以外还要将 vuexvue-router 升级至3.x的最新版本。主要是为了兼容 composition-api

<script setup>
import { useRouter } from 'vue-router/composables'

const router = useRouter()
</script>

vuex 并没有提供 useStore 方法,建议开发新功能时使用 pinia 而不是 vuex

三、eslint 相关的包 #

涉及范围:

"babel-eslint": "^10.1.0",
"eslint": "~7.32.0",
"eslint-plugin-vue": "~9.9.0",

项目中原先使用的是 eslint@6.7.2,此版本不支持可选链运算符,所以需要升级,至于为什么不升到最新版本,后面踩坑记录会解释。

babel-eslint 实际上已经弃用了,现在应该使用 @babel/eslint-parser,但盲目升级容易踩大坑,所以暂时将其升至弃用前的最新版本,等后续重构项目时再进行升级。

因为 vue2.7 支持 <script setup> 语法,所以要将 eslint-plugin-vue 升至9.x,也就是支持 vue3.x 的版本,否则 eslint 会报 no-unused-vars 的错误。

此外,本次升级还修改了部分 .eslintrc.js 配置,代码如下:

module.exports = {
  root: true,
  env: {
    node: true,
    es6: true,
  },
  extends: ['plugin:vue/essential', 'eslint:recommended', 'plugin:prettier/recommended'],
  plugins: ['prettier'],
  parser: 'vue-eslint-parser',
  parserOptions: {
    ecmaVersion: 2020,
    parser: 'babel-eslint',
    sourceType: 'module',
  },
  rules: {
    'prettier/prettier': ['error'],
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'vue/multi-word-component-names': 'off',
    'vue/no-reserved-component-names': 'off',
    'no-unused-vars': 'warn',
  },
}

四、sass相关的包 #

涉及范围:

"node-sass": "^6.0.1",
"sass-loader": "^10.2.1",

项目中原先使用的是 sass-loader@8.0.2webpack4 最高可支持版本是10.x,所以进行升级。

实际上 sass-loader9.x开始就推荐使用 dart-sass 来替代 node-sass,但由于项目中很多地方使用了 /deep/ 的写法,导致 dart-sass 解析不通过,所以暂时还是使用 node-sass,并将其升级到 sass-loader@10.x 所需的6.x版本。

五、集成windicss #

最简单的一个步骤:vue add windicss

官方文档:https://windicss.org/integrations/vue-cli.html

其实本来是想集成 unocss的,毕竟 windicss 已经停止维护了,但是 unocss 打包时会出现问题,查了许多资料都没解决,有能力的大佬可以尝试解决一下:

Error: CSS minification error: Cannot read properties of undefined (reading '0') 报错的插件是 optimize-cssnano-plugin

踩坑总结 #

一、集成unplugin-auto-import #

最开始的时候,我倾向于将 unplugin-auto-import 集成到项目中,毕竟在每个文件内手动导入 vue 的各种 API 确实有点麻烦。集成之后插件确实起作用了,不需要 import 就可以直接写 ref 也确实很舒适,但是 volarAuto Complete Refs 功能失效了(盲猜是 vue2.7 的原因),并且考虑到项目是团队协作开发,外部的东西有明确的来源才能更好地让其他人理解代码。于是最后还是决定移除 unplugin-auto-import

二、使用相对路径引用的图片解析后的地址不正确 #

项目中有一些图片是在 css 中使用相对路径引用的,比如:

.icon2front {
   z-index: 2;
   transform: rotateY(0deg);
   background-image: url('../../images/home-SZJX-icon2.png');
}

启动服务后类似这种相对路径引用的图片全都无法显示,而引用自 public 文件夹下的静态资源则没有问题。

打开控制台可以看到,使用相对路径的图片打包后的路径变成了 background-image: url(img/home-SZJX-icon2-sel.dea0b320.png); ,很明显是引用路径不对,正确的引用应该基于当前的文件路径 url(/img/home-SZJX-icon2-sel.dea0b320.png)

一顿乱七八糟的分析之后,发现问题应该是处在 url-loader 上,在 vue.config.js 中给 url-loader 配置了 publicPath = '/' 成功解决问题。

三、启动项目时报错TypeError: this.cliEngine is not a constructor #

这个就是前面提到的关于 eslint@8.x 的坑了。

原因是项目中使用了 vue-eslint-parser 作为 eslint 的 parser,而 vue-eslint-parser 最高仅支持 eslint@7.x,所以才会报错,将 eslint 降为7.x版本即可解决问题。

四、引入windicss后启动服务报错 #

启动项目后报 Syntax Error: Unterminated string constant,报错代码如下:

// import { replace } from 'lodsh-es'

return replace(
  this.laboratoryJupyterManualContent,
  '<body',
  '<style type="text/css">body {user-select: none;}</style><body'
)

看上去是把 js 代码中的 html 片段一起解析了,解决方法是使用模板字符串进行换行:

return replace(
  this.laboratoryJupyterManualContent,
  '<body',
  `<style type="text/css">
    body {user-select: none;}
   </style>
   <body`
)

五、引入windicss后首页样式不正确 #

原因是出在样式类名上,项目的首页自定义了一个叫做 container 的样式类,恰巧 windicss 中预设了一个 shortcuts 也叫 container ,于是 windicss 的样式就被 merge 进了项目里的自定义类,导致首页的样式不正确。解决方法是在 windicss.config.js 中配置一个 shortcuts 来覆盖掉预设:

import { defineConfig } from 'windicss/helpers'

export default defineConfig({
  preflight: false,
  plugins: [
    require('windicss/plugin/filters'),
    require('windicss/plugin/forms'),
    require('windicss/plugin/aspect-ratio'),
    require('windicss/plugin/line-clamp'),
    require('windicss/plugin/typography')({
      modifiers: ['DEFAULT', 'sm', 'lg'],
    }),
  ],
  shortcuts: {
    // 覆盖自带的shortcuts
    container: '',
  },
})

六、Jenkins打包报错 #

npm install 失败

原因:Jenkins 上的 Node版本使用的是16.xnpm 版本则是8.x,项目依赖冲突导致安装过程中断,解决方法是给 npm install 加上后缀 --legacy-peer-deps,详细原理可参考这篇文章

SyntaxError: Unexpected token u in JSON at position 0 vue.config.js

原因:项目中的 vue.config.js 使用了 process.env.npm_config_argv 全局变量,但这个变量从 npm@7.x 开始就被移除了。解决方法是将命令改为 npm run <command> -- --param=value 并使用 process.argv 来获取命令参数。但由于项目中并没有实际使用该变量(process.env.npm_config_argv),所以只是将其注释掉,并没有进行其他更改。

gyp ERR! build error

解决方法:将 Jenkins 上的 Node 版本降为14.x

总结 #

这次升级确实花了不少时间,时间长的原因:

  1. 各种小坑太多,以上列出的几个坑只是比较突出,实际上还有很多其他的坑会踩到。
  2. 需要做的取舍太多,来来回回试了好几种方案,其中 unocss 消耗的时间最长,可惜最终还是没能用上。
  3. 自身技术水平不够扎实,很多 webpack 的配置要查很久。
  4. 一开始没有做好规划,直接就上手改了,越改越乱,导致花费的时间暴涨。