拒絕gogocode,vue2升級vue3,看這里

聲明

請注意,筆者在寫本文時,已經(jīng)完成了升級改造工作,故部分錯誤可能無法通過截圖呈現(xiàn),不過筆者會盡可能用語言描述清楚

一次失敗的嘗試

由于所要改造項目是公司運(yùn)行六七年的老項目了,內(nèi)容之大之重可以想象,純靠人力的話,光是想想就頭大

所謂遇事不決先百度,像這類遷移工具必定是有人已經(jīng)做過了的,于是發(fā)現(xiàn)了gogocode

首先,使用npm安裝該工具

npm install gogocode-cli -g

接著進(jìn)行語法轉(zhuǎn)換

在項目根目錄執(zhí)行如下命令

// src是要轉(zhuǎn)換的源
gogocode -s ./src -t gogocode-plugin-vue -o ./src-out

然后靜靜等待它轉(zhuǎn)換完成

image.png

下一步,對相關(guān)依賴做升級,gogocode也提供了相關(guān)指令

gogocode -s package.json -t gogocode-plugin-vue -o package.json

這一步執(zhí)行之后,使用yarn重新安裝依賴

yarn

這樣工具幫我們處理的工作就完成了,我們先來跑一下lint看看,如下有3k+

image.png

此時,我已經(jīng)有點打退堂鼓了

先不急,咱們先來看看它轉(zhuǎn)出來的效果

首先,它對package.json轉(zhuǎn)換后,vue的版本是3.0.0。我為什么要特意強(qiáng)調(diào)這個呢?

因為這說明了兩個問題:

1.現(xiàn)在vue3.3了,它卻還在用3.0,這說明大概率它已經(jīng)鮮有維護(hù)更新了

2.3.3的語法與3.0大概率會存在出入,也就是說,它的轉(zhuǎn)換結(jié)果未必可信

現(xiàn)在,我們再來看具體的sfc文件的轉(zhuǎn)換

  • props

我們知道,vue3.3props已經(jīng)被扁平化了,是不需要通過特定的props屬性進(jìn)行傳遞的,但gogocode對這一部分并未處理

image.png
  • attrs

attrs也有類似的問題

image.png
  • style樣式

工具對源碼轉(zhuǎn)換后,會導(dǎo)致style標(biāo)簽內(nèi)的樣式錯亂,具體來說應(yīng)該是它無法識別css代碼中的注釋導(dǎo)致的

  • 格式

除了上述的語法轉(zhuǎn)換問題外,還有其他的,此處不再一一闡述。其實真正讓我決定放棄的是,它轉(zhuǎn)換出來感覺有點亂,且對源碼的侵入性太大

比如eventBus,修改后,無法和原來一樣調(diào)用,需要先導(dǎo)入,再使用,且必須手動傳入組件實例,偽代碼如下

import {$on,$emit,$off} from '../xx/utils'

$on(instance,key,callback)

這成功給筆者造成了將近一千個error,因為項目里用到了400多次

image.png

具體來說,每一個文件都會觸發(fā)相對路徑必須在絕對路徑之后引入變量未被使用這兩個error

半自動化

現(xiàn)在,筆者打算自己手動修正。不過在開始前,先說原則,原則就是,能不修改原來代碼的就盡量不修改,能保證原有調(diào)用格式的就盡量保證原來的使用方式

loaders

一些通用的調(diào)整,可以通過webpackloader來自動化

  • filters

vue3中已經(jīng)剔除了filters選項,需要將其移動到methods

image.png

不過,代碼有點多,咱這文章畢竟也不是賣錢的小冊,所以就只講一下思路,這也是最重要的:

首先,通過正則提取出script標(biāo)簽的內(nèi)容部分,然后將其ast化,在通過節(jié)點樹找到export default部分并將其提取出來轉(zhuǎn)換成對象

轉(zhuǎn)對象的時候,由于部分代碼可能是export default外部的,比如

const yyy = 'spp'
export default {
    data(){
        return xxx:yyy
    }
}

這部分直接去解析會報錯,因此需要進(jìn)行try...catch并在捕獲到錯誤時遞歸

function parse(){
    try{
        ...
    }catch(err){
        if(err){
            parse()
        }
    }
}

接著判斷對象中是否存在filters屬性,如果有,就對該屬性做遍歷,將其key提取到methods中,形式大概是這樣

export default {
    methods:{
        filtersKey:filtersValue
    }
}

這個filtersValue對應(yīng)的就是原來的過濾函數(shù),要把它生成到script標(biāo)簽中,具體來說,是import語句和export之間

function filtersValue(){
    ...
}

下一步,就是去template模板中查找使用過濾函數(shù)的地方進(jìn)行替換。這利用正則可以很輕松的完成

image.png
  • props

vue2中可以這樣聲明props

export default {
    props:{
        xxx:'some value'
    }
}

vue3中會報錯。因此需要對此進(jìn)行轉(zhuǎn)換。具體來說,就是找到props對象,分別對它的key對應(yīng)的值進(jìn)行識別。如果是對象或函數(shù)則不處理,如果是基本數(shù)據(jù)類型,則轉(zhuǎn)換成如下形式

{
    type:'原值所對應(yīng)的數(shù)據(jù)類型',
    default:'some value'
}

這里比較容易出錯的點是關(guān)于字符串類型的獲取,需要手動拼接上引號

image.png

完整代碼如下

image.png
  • bus通信

眾所周知,vue3中已經(jīng)剔除了eventBus,無法再通過new實例的方式來實現(xiàn)跨級通信

但第三方插件又無法保證this指向,一般需要手動傳入

但前文我們說過,要盡可能保證原調(diào)用方式不變

因此,需要在loader中提取并添加

image.png

這里的注意點在于loader的執(zhí)行時機(jī),必須要保證它在源代碼被轉(zhuǎn)換之前。否則添加的this與回調(diào)函數(shù)內(nèi)的this大概率不是一個。如下,是經(jīng)過轉(zhuǎn)換后的代碼,在回調(diào)中使用的this其實并不是實際傳入的this

var this1 = this
this.$bus.$on(key,this,()=>{
    this1.xxx = 'spp'
})

fixed

還有一些是可以在全局去做兼容的,它們的目的是與改造前的代碼行為盡可能保持一致

  • bus通信

在前文loader中雖然解決了eventBus的this指向問題,但還沒有找到可替代的包

幸運(yùn)的是,筆者在之前實現(xiàn)的web-localstorage-plus中實現(xiàn)了該功能

首先,使用web-localstorage-plus提供的接口來代替bus功能

image.png

接著將其掛載到app.config.globalProperties上,這是vue3中提供的類似Vue.prototype掛載方式

image.png

又不幸的是,web-localstorage-plus目前其實并不支持接收this參數(shù),且回調(diào)函數(shù)也不支持傳遞多個參數(shù)

因此,還需要對這兩個接口進(jìn)行重寫。如下,接受參數(shù)二并將其掛載到第三個函數(shù)參數(shù)上,然后在觸發(fā)on注冊的監(jiān)聽函數(shù)時手動使用call修改其this指向,并將參數(shù)使用展開運(yùn)算符傳遞以支持多個參數(shù)傳遞

image.png
  • vue-router

在筆者的業(yè)務(wù)中,對單點登錄進(jìn)行了校驗,并在無權(quán)限時跳轉(zhuǎn)到指定的頁面,偽代碼如下

// 獲取權(quán)限
this.$router.onReady(...)
// 校驗權(quán)限
this.$router.beforeEach(...)

這兩行代碼有兩個問題

一個是vue-router4.x中已經(jīng)廢棄了onReady,需要改成isReady代替

image.png

另一個問題是,當(dāng)頁面刷新或執(zhí)行window.open時,其表現(xiàn)與vue-router3.x不一致

vue-router3.x中刷新并不會觸發(fā)beforeEach鉤子,但vue-router4.x中會觸發(fā)

這就導(dǎo)致,會觸發(fā)權(quán)限的重新獲取,而此時beforeEach鉤子執(zhí)行時還拿不到權(quán)限數(shù)據(jù)導(dǎo)致跳轉(zhuǎn)到noAuth頁面

因此,需要對beforeEach鉤子進(jìn)行重寫。如下,我們在頁面刷新或window.open被執(zhí)行時向本地設(shè)置緩存,并在beforeEach鉤子中判斷是否存在緩存標(biāo)記,存在則什么都不做,否則正常跳轉(zhuǎn)路由

image.png
  • $children

vue3中已經(jīng)剔除了$children接口,需要自己手動實現(xiàn)一份查找邏輯。如下,只需要按指定的格式進(jìn)行遞歸即可

image.png
  • $filters

對于注冊的全局過濾器,現(xiàn)在統(tǒng)一調(diào)整到app.config.globalProperties

image.png
  • view-ui-plus

之前默認(rèn)注冊到全局的loading現(xiàn)在已經(jīng)沒有了,需要根據(jù)業(yè)務(wù)需求做調(diào)整

image.png

其他

剩下的,就是需要手動調(diào)整的其他語法了

  • vue-router

1-h函數(shù)

vue4.x版本中應(yīng)該已經(jīng)不支持render函數(shù)了,由于筆者公司項目有且僅有這一處對組件的使用,故修改成了默認(rèn)方式

image.png

實際上,更準(zhǔn)確的寫法應(yīng)該是這樣

image.png

2-注冊方式

4.x中已經(jīng)不需要使用new關(guān)鍵字了,取而代之的是createRouter接口。另外,mode屬性也被history替代了

import { createRouter, createWebHashHistory } from 'vue-router';
export default createRouter({
  history: createWebHashHistory(),
  routes:[...]
})
  • vuex

1-注冊方式

vue-router類似,通過createStore替換。

import { createStore } from 'vuex';
export default createStore({...})

2-日志引入

它的日志插件的導(dǎo)入方式要改成下邊這樣

import { createLogger } from 'vuex';
  • view-ui-plus

對于js模塊中使用的消息提示需要手動按需導(dǎo)入

import { Message } from 'view-ui-plus'

Message.error('...')
  • h函數(shù)

h函數(shù)的變動大致有四個

1-props與attrs

現(xiàn)在不需要在顯示的指定這兩個屬性了,可以直接平鋪傳遞

h('div',{
  //props:{
  //  xxx:'spp'
  //}
  xxx:'spp'
})

2-事件綁定

現(xiàn)在也已經(jīng)剔除了on屬性,而需要改成onEventType的形式

h('div',{
  //on:{
  //  click:()=>{}
  //}
  onClick(){}
})

3-slots

插槽需要改成函數(shù)形式

h('div',{
    //slot:'slotName'
    slotName(){}
})

4-組件渲染

現(xiàn)在使用h函數(shù)渲染組件有兩種方式,筆者采用的方式如下

首先將要導(dǎo)入的組件掛載到全局

import { LoadingBar } from 'view-ui-plus';

app.component('LoadingBar', LoadingBar);

然后借助vue3提供的resolveComponent來執(zhí)行導(dǎo)入

import { resolveComponent } from 'vue';
h(resolveComponent('LoadingBar'))
  • $set

基于proxyv3已經(jīng)不需要$set或$delete了,現(xiàn)在直接進(jìn)行修改即可

  • slot

v3中的插件必須使用template,且寫法有變更,如下,現(xiàn)在可以合寫成一個了

<template 
  //slot="createTime" 
  //slot-scope="scope"
  v-slot="scope"
>
  ...
</template>
  • 三方組件庫

這種每個人的不一樣,筆者就不貼了,只說下筆者的處理方式

1-找vue3版本

有些組件庫是支持vue3版本的,這部分直接yarn即可

2-修改源碼

如果沒有v3版本,就利用patch-pkg自己修改源碼做兼容

3-copy源碼

對于比較復(fù)雜的庫,筆者是找到其源代碼copy了一份,然后在項目中引入并修改

  • vue-loader

項目啟動后,頁面元素之間的間隙會失效,配置如下

image.png

測試問題修復(fù)

  • vue-router傳參

4.x已經(jīng)不支持傳遞對象形式了

image.png

因為拿到的是字符串,并且這個字符串還沒法通過JSON.parse進(jìn)行解析

image.png

但是又沒法一個一個調(diào)整,畢竟有一百多處都在使用

image.png

所以還是老規(guī)矩,從源碼修改。這只需要分兩步

第一步,重寫push

image.png

第二步,重寫query屬性

image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容