Vue2升级Vue3 --- Vue变更(基于gogocode)

本次升级基于gogocode快捷转换gogocode升级体验,汇总的部分问题可能与gogocode有关,但仍可作为vue2升级vue3过程中报错问题的解决参考

Vue变更

1.实例创建与全局实例变量

window.$vueApp = Vue.createApp(App)
window.$vueApp.use(router) // router在挂载之前,否则无法渲染
window.$vueApp.mount('#app')
window.$vueApp.use(store)
window.$vueApp.config.globalProperties.$http = axios

2.路由定义

// Catch all routes ("*") must now be defined using a param with a custom regexp.
path: '*' => path: '/:catchAll(.*)'

component: (resolve) => {
    require.ensure([], () => {
        return resolve(require('@views/login/Index.vue'))
    }, 'login')
}
=>
component: () => import('@views/login/Index.vue'),

3.渲染函数

// 需手动全局引入h渲染函数
import { h } from 'vue'
createElement => h

// 渲染函数传参全部扁平化,将外层包裹去掉
h(CtrlViewMap[type] || CtrlViewMap.native, {
    protocol: p,
    uitype,
    fixedStyle: style,
    clickCtrlView: this.clickCtrlView,
    eventScroll: (e, layoutScrollTop) => {
        this.$emit('event-scroll', e, layoutScrollTop)
    },
    key: p.code,
    class: isHidden
})

4.插槽

// Vue3中插槽获取的是插槽渲染函数,不再是Vue2中直接的虚拟节点VNode
this.$slots.default => this.$slots.default()
this.$slots['default'] => this.$slots['default']()

// scopedSlots统一替换为slots
scopedSlots => slots

// 插槽变量采用对象解构
"text, record" => "{text, record}"

5.数组监听

  • 问题:gogocode在对数组监听函数进行语法转换时增加deep选项深度监听,但项目中不需要深度监听,增加deep选项反而导致报错(涉及Vue3中数组监听没有deep情况下只有数组替换才触发监听,数组变化不触发)
  • 报错:TypeError: Cannot set properties of null (setting '__vnode') at patchElement (runtime-core.esm-bundler.js:4626:1)
  • 解决:去除deep字段

6.事件总线

  • gogocode快捷转换现状
    • 生成gogocodeTransfer文件并局部引入,会生成多个相同文件且转化失败的文件需要手动引入,可尝试全局引入该方法
    • 部分API转换失败需要手动转化,转换成功的大部分是不需要转化的父组件this.$emit事件
  • 基于gogocode快捷转换后进行手动调整
/*
正则替换:`'[^']*gogocodeTransfer[^']*'`替换为`'@/utils/gogocodeTransfer'`
批量删除gogocodeTransfer引入
批量替换转换后的$on(Bus)/$off(Bus)/$emit(Bus)/$once(Bus)恢复为Bus.$on/Bus.$off/Bus.$emit/Bus.$once,部分链式调用需要注意单个恢复
*/

// main/index
import { $on, $off, $emit, $once} from '@/utils/gogocodeTransfer'
// 兼容this未转换的指令
window.$vueApp.config.globalProperties.$on = $on
window.$vueApp.config.globalProperties.$off = $off
window.$vueApp.config.globalProperties.$once = $once
window.$vueApp.config.globalProperties.$emit = $emit
// 兼容转换后的$emit(this),本身并不需要转换,vue3仍支持this.$emit触发子组件事件
window.$emit = $emit
window.$on = $on
window.$off = $off
window.$once = $once



// bus/index(对原事件总线改造而不使用gogocode转换)
import * as Vue from 'vue'
const busInstance = {}
const eventPool = {}
const eventRegistryMap = new WeakMap()
function getRegistry() {
  let events = eventRegistryMap.get(busInstance)
  if (!events) {
    eventRegistryMap.set(busInstance, (events = Object.create(null)))
  }
  return events
}
function addEvent(eventName, fn) {
  if (!fn) {
    return
  }
  if (!eventPool[eventName]) {
    eventPool[eventName] = []
  }
  eventPool[eventName].push(fn)
}

function removeEvent(eventName, fn) {
  if (!eventPool[eventName]) return
  if (!fn) {
    delete eventPool[eventName]
    return
  }
  const events = eventPool[eventName].filter((event) => event !== fn)
  eventPool[eventName] = events
}

busInstance.$on = function (eventName, fn) {
  addEvent(eventName, fn)
  if (Array.isArray(eventName)) {
    eventName.forEach((e) => busInstance.$on(e, fn))
  } else {
    const events = getRegistry()
    if(events && events[eventName]) events[eventName].push(fn)
    else if(events){
      events[eventName] = []
      events[eventName].push(fn)
    }
  }
  return busInstance
}

busInstance.$off = function (eventName, fn) {
  removeEvent(eventName, fn)
  const vm = busInstance
  // all
  if (!eventName) {
    eventRegistryMap.set(busInstance, Object.create(null))
    return vm
  }
  // array of events
  if (Array.isArray(eventName)) {
    eventName.forEach((e) => busInstance.$off(e, fn))
    return vm
  }
  // specific event
  const events = getRegistry()
  const cbs = events[eventName]
  if (!cbs) {
    return vm
  }
  if (!fn) {
    events[eventName] = undefined
    return vm
  }
  events[eventName] = cbs.filter((cb) => !(cb === fn || cb.fn === fn))
  return vm
}

busInstance.$once = function (eventName, fn) {
  const wrapped = (...args) => {
    busInstance.$off(eventName, wrapped)
    fn.call(busInstance, ...args)
  }
  wrapped.fn = fn
  busInstance.$on(eventName, wrapped)
  return busInstance
}

busInstance.$emit = function (eventName, ...args) {
  const cbs = getRegistry()[eventName]
  if (cbs) {
    cbs.map((cb) => cb.apply(busInstance, args))
  }
  return busInstance
}

busInstance.$getOns = (eventName) => {
  if (!eventName) {
    return eventPool
  }
  return eventPool[eventName] || []
}

export default busInstance

7.各类变化相关

  • Vue3中对象新增删除属性会触发响应式更新,若项目中存在对象新增删除属性,需注意Vue3触发响应式更新是否会影响当前逻辑
  • 若实例中未显式定义components,this.$options中无法获取到components
  • 异步组件不再是函数形式引入,无法作为函数执行
  • $mount在Vue3不支持
  • iframe赋值为非响应式变量造成的跨域报错Failed to read a named property '__v_isRef' from 'Window': Blocked a frame with origin "http://127.0.0.1:9088" from accessing a cross-origin frame.(暂不清楚原因,通过赋值为响应式变量解决)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值