2023年初,我的Vue2.7升级实践踩坑总结

7,340 阅读3分钟

前言

Vue3 发布作为默认版本将近一年了,手里的 Vue2 项目,希望最低成本的能使用 Vue3 的新特性:Composition Api、组合式 API、v-bind() in CSS、template 中的可选链等。正好年初事情较少,因此决定升级至 Vue2.7,该版本支持 Vue3 中一些最重要的功能,让 Vue2 的用户也能从中受益,并为可能迁移到 Vue3 做好更好的准备。

2022 年 7 月 1 号,Vue2.7 正式发布,Vue2.7 是 Vue2.X 的最终次要版本,在这个版本之后,Vue2 将进入 LTS(长期支持),即从发布开始持续 18 个月,Vue2 将不再接收新功能。

本文记录升级过程并总结一些经验,欢迎感兴趣的小伙伴阅读。

项目版本:vue@2.6.14 vue-cli@4.4.0

Vue2.7 新特性介绍及官方指南:blog.vuejs.org/posts/vue-2…

具体实践:

1.vue、@vue/cli-xxx 更新

  1. v4 版本的@vue/cli-xxx,升级到~4.5.18

  2. v5 版本的@vue/cli-xxx,升级到~5.0.6

// package.json
{
    "dependencies": {
        // "vue": "2.6.14",
        "vue": "2.7.0"
    }
    "devDependencies": {
        // "@vue/cli-plugin-babel": "~4.4.0",
        // "@vue/cli-plugin-eslint": "~4.4.0",
        // "@vue/cli-plugin-router": "^4.4.0",
        // "@vue/cli-plugin-vuex": "^4.4.0",
        // "@vue/cli-service": "~4.4.0",
        "@vue/cli-plugin-babel": "~4.5.18",
        "@vue/cli-plugin-eslint": "~4.5.18",
        "@vue/cli-plugin-router": "~4.5.18",
        "@vue/cli-plugin-vuex": "~4.5.18",
        "@vue/cli-service": "~4.5.18",
        //2.7版本中不再需要`vue-template-compiler`,可移除
        // 注意:如果项目中引入了`@vue/test-utils`,则需要保留`vue-template-compiler`
        // "vue-template-compiler": "2.6.14",
    }
}

2.删除 node_modules 和 package-lock.json

为防止某些未列出的依赖项(如下)不满足版本要求,建议删除node_modulespackage-lock.json,并重新 install,以确保升级到最新版本。

-   `vue-loader``^15.10.0`
-   `vue-demi``^0.13.1`

3.@vue/composition-api

项目中已经使用了@vue/composition-api,则移除 main.js 的注册代码,并将相应 import 导入更新为vue

注:2.7 版本未移植的一些 API(例如createApp)仍然需要使用@vue/composition-api

// main.js
// import VueCompositionAPI from '@vue/composition-api'
// Vue.use(VueCompositionAPI)

// 引入的地方
// import { ref, reactive } from '@vue/composition-api'
import { ref, reactive } from 'vue'

4.eslint-plugin-vue 升级 v9+

如果使用<script setup>有未使用的变量 lint 错误提示,需要升级eslint-plugin-vue至 9+版本。

升级后项目启动报错,原因:配置了多余参数:

image.png

官网(eslint.vuejs.org/rules/max-a… )查了下,没有了 allowFirstLine 的配置,删除后正常运行。

5.深度选择器改写::v-deep、/deep/为:deep()

更新后,如果有::v-deep、/deep/相关的报错或者警告,需要改用:deep()

<style scoped>
  .a :deep(.b) { /* ... */ }
</style>

6.vue2.7 与@vue/composition-api 的 setup 参数差异

之前使用@vue/composition-ap的时候有用到 root 调用全局定义的方法:

setup(props,{ root }){
 // 调用全局定义的方法
 root.xxxx
}

在升级后发现 setup 第二个参数将 root 等属性移除了, 原先 setup 第二个参数包含

setup(props, { slots, emit, attrs, root, parent, refs, listeners, isServer, ssrContext })
image.png image.png

从截图的createSetupContext方法中可以看到 vue2.7 只包含了attrs, listeners, slots, emit, expose;在 vue3 中 listeners也被移除。
解决方案:使用了getCurrentInstance方法替代(后面会对该方法介绍)。

import { getCurrentInstance } from 'vue'
...
setup(){
  const root = getCurrentInstance().proxy
}

7.setup 中使用 vuex、vue-router

由于项目版本 vuex、vue-router 均为 v3,组合式 API 中,我们需要使用一些新的函数来代替访问  this等方法,如:this.$store、this.$router、this.$route
解决方案:也用到了 getCurrentInstance,通过它封装一些方法使用。

import { getCurrentInstance } from 'vue'

export function useStore() {
  const { proxy } = getCurrentInstance()
  const store = proxy.$store
  return store
}
export function useRoute() {
  const { proxy } = getCurrentInstance()
  const route = proxy.$route
  return route
}
export function useRouter() {
  const { proxy } = getCurrentInstance()
  const router = proxy.$router
  return router
}

还有一些其他写法可以参考此 issue:github.com/vuejs/vue-r…

然而组合式 API 下 vuex 的mapStatemapGettersmapActions  和  mapMutations  辅助函数依然是无法使用的。可以尝试vuex-composition-helpers这个库,帮助轻松使用 Vuex 和 Composition API。

补充:关于 getCurrentInstance

注意:getCurrentInstance 作为访问内部组件实例的方法,官方是不鼓励在应用程序代码中使用的,下面贴上官方原文及例子:

image.png

import { getCurrentInstance } from 'vue'

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance()

    internalInstance.appContext.config.globalProperties // access to globalProperties
  }
}

并且 getCurrentInstance 只在 setup 或生命周期钩子中工作:

image.png

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance() // works

    const id = useComponentId() // works

    const handleClick = () => {
      getCurrentInstance() // doesn't work
      useComponentId() // doesn't work

      internalInstance // works
    }

    onMounted(() => {
      getCurrentInstance() // works
    })

    return () =>
      h(
        'button',
        {
          onClick: handleClick
        },
        `uid: ${id}`
      )
  }
}

// also works if called on a composable
function useComponentId() {
  return getCurrentInstance().uid
}

摘录自:github.com/vuejs/docs/…

总结

Vue2.7 版本的升级过程总的来说,还算顺利。感谢大家阅读,如有补充或者文中有描述不正确的地方,欢迎留言交流。