vue語(yǔ)法自動(dòng)轉(zhuǎn)typescript(解放雙手)

代碼的復(fù)用是一件很常見(jiàn)的事情,如果是公共代碼的復(fù)用那還好說(shuō),直接做成一個(gè)內(nèi)部私有庫(kù),想用的話(huà)安裝一下 npm包就行了,但是業(yè)務(wù)代碼的復(fù)用就不好做成包了,一般都是復(fù)制粘貼

成都創(chuàng)新互聯(lián)公司是一家專(zhuān)注于網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)與策劃設(shè)計(jì),六盤(pán)水網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)十年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:六盤(pán)水等地區(qū)。六盤(pán)水做網(wǎng)站價(jià)格咨詢(xún):18982081108

我一般寫(xiě)代碼的時(shí)候,如果覺(jué)得某段業(yè)務(wù)代碼以前見(jiàn)過(guò)其他人寫(xiě)過(guò),那么考慮到業(yè)務(wù)優(yōu)先性,只要?jiǎng)e人的代碼不是寫(xiě)得太爛,我一般會(huì)優(yōu)先抄別人的代碼,省得自己再寫(xiě)一遍

然后我就遇到了一個(gè)問(wèn)題,公司目前前端項(xiàng)目大部分都是 vue,早期沒(méi)有 ts這個(gè)說(shuō)法,后來(lái)新項(xiàng)目才逐漸引入 ts,所以新項(xiàng)目用的是 vue-ts,而一般想抄的老代碼都是沒(méi)有引入 ts的,固然,這二者是可以兼容存在的,但對(duì)于有著輕微代碼潔癖的我來(lái)說(shuō),還是不想看到同一個(gè)項(xiàng)目代碼里摻雜著 ts和非 ts兩種寫(xiě)法的,所以只要有時(shí)間,我都會(huì)盡量手動(dòng)把老代碼轉(zhuǎn)化為 ts規(guī)范的
難度倒是沒(méi)多少,只不過(guò)每一份都要手動(dòng)轉(zhuǎn)一遍,轉(zhuǎn)得多了我忽然陷入沉思,我好像 repeat myself了啊,不太能忍,于是決定寫(xiě)一個(gè)自動(dòng)將 vue-js轉(zhuǎn)成 vue-ts的工具

這個(gè)工具的代碼已經(jīng)被我放到 github 上了,并且為了方便使用,我已經(jīng)將其做成了一個(gè) npm 包,感興趣的可以親自試一下

@babel

涉及到 js語(yǔ)法轉(zhuǎn)換的東西,第一時(shí)間想到的就是 babel了,babel早就提供了豐富完善的 js語(yǔ)法的解析與反解析工具

@babel/parser

@babel/parser 是負(fù)責(zé)解析 js語(yǔ)法的工具,可以理解為將 js語(yǔ)法轉(zhuǎn)化為 ast,方便開(kāi)發(fā)者進(jìn)行自定義處理,通過(guò) plugins來(lái)支持多種 js語(yǔ)法,例如 es6、es7、ts、flow、jsx甚至是一些實(shí)驗(yàn)室的語(yǔ)法(experimental language proposals)等

例如:

const code = 'const a = 1'
const ast = require("@babel/parser").parse(code)

轉(zhuǎn)換后的 ast就是一個(gè)對(duì)象,數(shù)據(jù)結(jié)構(gòu)描述的就是 const a = 1這個(gè)表達(dá)式

vue語(yǔ)法自動(dòng)轉(zhuǎn)typescript(解放雙手)

對(duì)這個(gè) ast進(jìn)行遍歷,就可以獲得所有當(dāng)前解析的 js語(yǔ)法的信息,自然也能對(duì)其進(jìn)行修改

@babel/generator

有解析就有反解析, @babel/generator用于將@babel/parser解析出的 ast轉(zhuǎn)化回字符串形式的 js代碼

const code = 'const a = 1;'
const ast = require("@babel/parser").parse(code)
const codeStr = require('@babel/generator').default(ast).code
code === codeStr // => true

其他

一般 @babel/parser、@babel/generator 和 @babel/traverse會(huì)一起出現(xiàn)使用,前兩個(gè)前面已經(jīng)介紹過(guò)了,至于 @babel/traverse,其主要作用就是對(duì) @babel/parser生成的 ast進(jìn)行遍歷,提供了一些方法,省得開(kāi)發(fā)者自己去做各種判斷

不過(guò)我這里寫(xiě)的這個(gè)程序,因?yàn)椴恍枰^(guò)細(xì)致的解析,所以沒(méi)用@babel/traverse這個(gè)東西,我按照自己的意愿對(duì) ast進(jìn)行遍歷操作

除此之外,babel還提供了一些其他的工具庫(kù)啦幫助庫(kù)啦,一般都不太用得到,想要詳細(xì)了解的可以自己去看文檔

本文下面所說(shuō)的操作,基本上都是在 @babel/parser 轉(zhuǎn)換后的 ast,以及 @babel/generator 解析后的代碼字符串上進(jìn)行的

props

vue官網(wǎng)對(duì)于 props的介紹在props

因此 props的以下幾種寫(xiě)法都是符合規(guī)范的:

export default {
 props: ['size', 'myMessage'],
 props: {
  a: Number,
  b: [Number, String],
  c: 'defaultValue',
  d: {
   type: [Number, String]
  }
  e: {
   type: Number,
   default: 0,
   required: true,
   validator: function (value) {
    return value >= 0
   }
  }
 }
}

上述轉(zhuǎn)換為 ts對(duì)應(yīng)如下:

export default class YourComponent extends Vue {
 @Prop() readonly size: any | undefined
 @Prop() readonly myMessage: any | undefined
 @Prop({ type: Number }) readonly a: number | undefined
 @Prop([Number, String]) readonly b: number | string | undefined
 @Prop() readonly c!: any
 @Prop({ type: [Number, String] }) readonly d: number | string | undefined
 @Prop({ type: Number, default: 0, required: true, validator: function (value) {
  return value >= 0
 } }) readonly e!: number
}

ok,那就好辦了,首先 props值的類(lèi)型只有 Array<string> 和 對(duì)象 這兩種類(lèi)型

數(shù)組類(lèi)型

Array<string>類(lèi)型很好辦,就一個(gè)轉(zhuǎn)換模板:

@Prop() readonly propsName: any | undefined

只需要遍歷 Array<string>類(lèi)型的 props,然后,把 propsName替換成真正的值即可

對(duì)象類(lèi)型

對(duì)象類(lèi)型的轉(zhuǎn)化模板在數(shù)組類(lèi)型的模板上,多加了一些字符串,主要就是 @Prop的參數(shù):

@Prop({ type: typeV, default: defaultV, required: requiredV, validator: validatorV }) readonly propsName: typeV

props 這個(gè)大對(duì)象的每個(gè)屬性,都是一個(gè) propsName,這個(gè)是確定的,然后 propsName對(duì)應(yīng)的值,可能是 type,type 分為單類(lèi)型(例如 Number),以及類(lèi)型數(shù)組(例如 [Number, String]);可能是一個(gè)對(duì)象,這個(gè)對(duì)象下的屬性最少為 0個(gè),最多為 4個(gè),如果這個(gè)對(duì)象存在一個(gè)屬性名為 type的屬性,則這個(gè)屬性的值也需要判斷單類(lèi)型和類(lèi)型數(shù)組,其他屬性直接取原值即可

無(wú)論 props對(duì)象的屬性值是對(duì)象還是 type,都需要處理 type,所以一個(gè)專(zhuān)門(mén)處理 type的方法 handlerType

如此一來(lái),如果是 type,則 handlerType直接處理好;如果是對(duì)象,則遍歷這個(gè)對(duì)象的屬性,發(fā)現(xiàn)屬性是 type,則調(diào)用

handlerType進(jìn)行處理,否則直接原樣作為 @Prop的參數(shù)即可

data

vue官網(wǎng)對(duì)于 data的介紹在data

data的類(lèi)型可以是 Object 或 Function,即以下幾種寫(xiě)法都合法:

export default {
 data: {
  a: 1
 },
 data () {
  return {
   a: 1
  }
 },
 data: function () {
  return {
   a: 1
  }
 }
}

上述轉(zhuǎn)換為 ts對(duì)應(yīng)如下:

export default class YourComponent extends Vue {
 a: number = 1
}

所以這里就很明了了,就是取 data返回值對(duì)象的每個(gè)屬性,作為 class的屬性,好像轉(zhuǎn)換一下就行了

但是,data其實(shí)還可以這么寫(xiě):

export default {
 data () {
  const originA = 1
  return {
   a: originA
  }
 }
}

當(dāng) data是 Function 類(lèi)型時(shí),在 return之前,還可以運(yùn)行一段代碼,這段代碼的運(yùn)行結(jié)果可能影響到 data的值

這種寫(xiě)法并不少見(jiàn),所以不可忽視,但如何處理 return之前的代碼?

我的做法是將 return之前的代碼放到 created生命周期函數(shù)中,并且在 created中的這些代碼之后,再對(duì)每個(gè) data重新賦一遍值
比如,對(duì)于上面的代碼來(lái)說(shuō),轉(zhuǎn)換成 ts,可以這么做:

export default class YourComponent extends Vue {
 a: any = null
 created () {
  const originA = 1
  this.a = originA
 }
}

所以,這就又涉及到 data對(duì) created的數(shù)據(jù)修改了,這里可以考慮強(qiáng)制先處理 data,但是我看了下,其實(shí)這里寫(xiě)兩段邏輯也并不復(fù)雜,所以我就不嚴(yán)格規(guī)定處理的順序了

model

vue官網(wǎng)對(duì)于 model的介紹在 model

model中引用了 props中的值,所以 model的使用其實(shí)是需要 props配合的

export default {
 model: {
  prop: 'checked',
  event: 'change'
 },
 props: {
  checked: {
   type: Boolean
  }
 }
}

上述轉(zhuǎn)換為 ts對(duì)應(yīng)如下:

export default class YourComponent extends Vue {
 @Model('change', { type: Boolean }) readonly checked!: boolean
}

可見(jiàn),@Model是具備聲明 props的功能的,在 @Model中聲明了的 props,就沒(méi)必要在 @Prop中再聲明一遍了,所以我這里安排了一下處理順序,先處理 model,再處理 props,并且在處理 props的時(shí)候,將 model中已經(jīng)聲明了的 props篩選掉

當(dāng)然,你也可以不專(zhuān)門(mén)先處理 model再處理 props,只要在處理 model的時(shí)候判斷一下,是否在此之前已經(jīng)處理過(guò) props了,根據(jù)結(jié)果來(lái)做相應(yīng)的處理流程,但這樣未免有些麻煩,需要根據(jù) props的處理與否來(lái)寫(xiě)兩段邏輯,這兩段邏輯比上面 data影響 created的要復(fù)雜一些,所以這里我就直接按照順序處理了,省得給自己找麻煩

computed

vue官網(wǎng)對(duì)于 model的介紹在 computed

以下幾種 computed的寫(xiě)法都是正確的

export default {
 computed: {
  a () { return true },
  b: function () { return true },
  d: {
   get () { return true },
   set: function (v) { console.log(v) }
  }
 }
}

vue-property-decorator并沒(méi)有提供專(zhuān)門(mén)的用于 computed的修飾器,因?yàn)?ES6的 get/set語(yǔ)法本身就可以替代 computed
上述轉(zhuǎn)換為 ts對(duì)應(yīng)如下:

export default class YourComponent extends Vue {
 get a () { return true }
 get b () { return true },
 get d (){ return true },
 set d (v) { console.log(v) }
}

除此之外,computed其實(shí)還支持箭頭函數(shù)的寫(xiě)法:

export default {
 computed: {
  e: () => { return true }
 }
}

但是 class語(yǔ)法的 get/set不支持箭頭函數(shù),所以不好轉(zhuǎn)換,另外因?yàn)榧^函數(shù)會(huì)改變 this的指向,而 computed計(jì)算的就是當(dāng)前 vue實(shí)例上的屬性,所以一般也不推薦在 computed中使用箭頭函數(shù),固然你可以在箭頭函數(shù)的第一個(gè)參數(shù)上獲得當(dāng)前 vue實(shí)例,但這就未免有點(diǎn)多此一舉的嫌疑了,所以我這里略過(guò)對(duì)箭頭函數(shù)的處理,只會(huì)在遇到 computed上的箭頭函數(shù)時(shí),給你一個(gè)提示

watch

vue官網(wǎng)對(duì)于 watch的介紹在watch

以下都是合法的 watch寫(xiě)法:

export default {
 watch: {
  a: function (val, oldVal) {
   console.log('new: %s, old: %s', val, oldVal)
  },
  // 方法名
  b: 'someMethod',
  // 該回調(diào)會(huì)在任何被偵聽(tīng)的對(duì)象的 property 改變時(shí)被調(diào)用,不論其被嵌套多深
  c: {
   handler: function (val, oldVal) { /* ... */ },
   deep: true
  },
  // 該回調(diào)將會(huì)在偵聽(tīng)開(kāi)始之后被立即調(diào)用
  d: {
   handler: 'someMethod',
   immediate: true
  },
  e: [
   'handle1',
   function handle2 (val, oldVal) { /* ... */ },
   {
    handler: function handle3 (val, oldVal) { /* ... */ },
    immediate: true
   }
  ],
  // watch vm.e.f's value: {g: 5}
  'e.f': function (val, oldVal) { /* ... */ }
 }
}

上述轉(zhuǎn)換為 ts對(duì)應(yīng)如下:

export default class YourComponent extends Vue {
 @Watch('a')
 onAChanged(val: any, oldVal: any) {}
 @Watch('b')
 onBChanged (val: any, oldVal: any) {
  this.someMethod(val, oldVal)
 }
 @Watch('c', { deep: true })
 onCChanged (val: any, oldVal: any) {}
 @Watch('d', { deep: true })
 onDChanged (val: any, oldVal: any) {}
 @Watch('e')
 onE1Changed (val: any, oldVal: any) {}
 @Watch('e')
 onE2Changed (val: any, oldVal: any) {}
 @Watch('e', { immediate: true })
 onE3Changed (val: any, oldVal: any) {}
 @Watch('e.f')
 onEFChanged (val: any, oldVal: any) {}
}

寫(xiě)法還是很多的,所以判斷分支肯定少不了

watch下的每個(gè)屬性都是一個(gè)需要進(jìn)行 watch的 vue響應(yīng)值,這些屬性的值可以是字符串、函數(shù)、對(duì)象和數(shù)組,共四種類(lèi)型
其中,字符串類(lèi)型就是相當(dāng)于調(diào)用當(dāng)前 vue實(shí)例里的方法,函數(shù)類(lèi)型就是調(diào)用這個(gè)函數(shù),比較簡(jiǎn)單;

對(duì)于對(duì)象類(lèi)型,其具有三個(gè)屬性:handler、deep、immediate,三個(gè)屬性都是可選,其中 handler的值是函數(shù)或字符串,其他兩個(gè)屬性的值都是 boolean類(lèi)型;

對(duì)于數(shù)組類(lèi)型,其每一個(gè)數(shù)組項(xiàng),其實(shí)都相當(dāng)于是字符串類(lèi)型、函數(shù)類(lèi)型和對(duì)象類(lèi)型的聚合,所以實(shí)際上只要處理這三種類(lèi)型即可,數(shù)組類(lèi)型則直接遍歷數(shù)組項(xiàng),每個(gè)數(shù)組項(xiàng)的類(lèi)型肯定在這三個(gè)類(lèi)型之內(nèi),按照類(lèi)型調(diào)用相應(yīng)的處理方法即可。

這是主體部分,除此之外,還需要考慮 handler函數(shù)的形式,以下幾種函數(shù)的寫(xiě)法都是合法的:

export default {
 watch: {
  a: function {},
  b () {},
  c: () => {},
  d: async function {},
  e: async () => {}
 }
}

不僅在 watch里面,其他一些 vue實(shí)例屬性,比如 created、computed等,只要是可能出現(xiàn)函數(shù)的地方,都需要考慮到這些寫(xiě)法
當(dāng)然,除此之外,還有 Generator函數(shù),但我這里不考慮,有更好的 async/await可用,為什么非要用 Generator

methods

vue實(shí)例的方法,都作為 methods這個(gè)對(duì)象的屬性存在,每個(gè)方法都是一個(gè)函數(shù),所以只需要將原 methods下的所有方法取出,轉(zhuǎn)換為 class的方法即可,沒(méi)什么工作量

不過(guò)需要注意的是,函數(shù)的寫(xiě)法有很多,還可以支持 async/await,這些寫(xiě)法都需要考慮到

lifeCycle

vue的生命周期鉤子函數(shù)有很多,還有一些第三方的鉤子函數(shù),例如 vue-router:

const vueLifeCycle = ['beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'activated', 'deactivated', 'beforeDestroy', 'destroyed', 'errorCaptured', 'beforeRouteEnter', 'beforeRouteUpdate', 'beforeRouteLeave']

這些鉤子函數(shù)其實(shí)就是函數(shù),跟 methods的處理方法一樣

component

這個(gè)比較簡(jiǎn)單,轉(zhuǎn)化一下然后拼接

export default {
 components: {
  a: A,
  B
 },
}

上述轉(zhuǎn)換為 ts對(duì)應(yīng)如下:

@Component({
 components: {
  a: A,
  B
 }
})
export default class TransVue2TS extends Vue {}

所以就是把原 components的屬性全部映射一遍即可

mixins

vue官網(wǎng)對(duì)于 mixins的介紹在 mixins

其值類(lèi)型為 Array<Object>

export default {
 mixins: [A, B]
}

上述轉(zhuǎn)換為 ts對(duì)應(yīng)如下:

export default class TransVue2TS extends Mixins(A, B) {}

原本 extends Vue改成 extends Mixins,并且 Mixins的參數(shù)就是原 mixins的所有數(shù)組項(xiàng)

provide && inject

當(dāng)我考慮如何處理這兩個(gè)的時(shí)候,看了下 vue官網(wǎng),官網(wǎng)上對(duì)于這兩個(gè)是這么說(shuō)的:

provide 和 inject 主要為高階插件/組件庫(kù)提供用例。并不推薦直接用于應(yīng)用程序代碼中。

并且在這段話(huà)上,還專(zhuān)門(mén)用紅色感嘆號(hào)標(biāo)識(shí)了一下,說(shuō)白了就是不建議你在業(yè)務(wù)代碼中,因?yàn)檫@不利于數(shù)據(jù)的追蹤,完全可以使用成熟的 vueBus或者 vuex代替,一般也不會(huì)用到這個(gè)東西的,我寫(xiě)這個(gè)轉(zhuǎn)換程序也是為了轉(zhuǎn)換業(yè)務(wù)代碼,所以我沒(méi)有對(duì)這兩個(gè)做處理,如果發(fā)現(xiàn)代碼中存在這兩個(gè)屬性,會(huì)提示你自己手動(dòng)處理

emit && ref

這兩個(gè)都只是一種類(lèi)似語(yǔ)法糖的東西,可以不做處理

文件處理

上述是針對(duì)一份 .vue文件的詳細(xì)處理的邏輯,想要真正的接入實(shí)際文件乃至文件夾的處理,自然少不了文件的讀取和更新操作,這就涉及到 node的文件處理內(nèi)容了,不過(guò)并不復(fù)雜,就不多說(shuō)了

npm 包

代碼寫(xiě)完之后,為了簡(jiǎn)化使用流程,我將其打包成了一個(gè) npm包上傳到 npm上去了,想要使用的話(huà),只需要下載這個(gè)包,然后在命令行中輸入指令即可

npm i transvue2ts -g

安裝完之后,默認(rèn)是跟 vue-cli一樣,會(huì)把此庫(kù)的路徑寫(xiě)到系統(tǒng)的 path中,直接打開(kāi)命令行工具即可使用,同時(shí)支持單文件和文件目錄耳朵轉(zhuǎn)化transvue2ts是庫(kù)的指令,第二個(gè)參數(shù)是需要處理的文件(夾)的 完整全路徑例如:處理 E:\project\testA\src\test.vue文件:

transvue2ts E:\project\testA\src\test.vue
=>
輸出路徑:E:\project\testA\src\testTs.vue

處理 E:\project\testA\src文件夾下的所有 .vue文件:

transvue2ts E:\project\testA\src
=>
輸出路徑:E:\project\testA\srcTs

對(duì)于單文件來(lái)說(shuō),其必須是 .vue結(jié)尾,轉(zhuǎn)化后的文件將輸出到同級(jí)目錄下,文件名為原文件名 + Ts,例如 index.vue => indexTs.vue;對(duì)于文件目錄來(lái)說(shuō),程序?qū)?huì)對(duì)此文件目錄進(jìn)行遞歸遍歷,找出這個(gè)文件夾下所有的 .vue文件進(jìn)行轉(zhuǎn)化,轉(zhuǎn)化后的文件將按照原先的目錄結(jié)構(gòu)全部平移到同級(jí)目錄下的一個(gè)新文件夾中,例如 /src => /srcTs

總結(jié)

這個(gè)轉(zhuǎn)化程序看起來(lái)很麻煩的樣子,概括一下,其實(shí)就三步:

  1. 列舉所有需要進(jìn)行轉(zhuǎn)化的 vue-js語(yǔ)法及其多變的寫(xiě)法
  2. 列舉 js-ts語(yǔ)法之間的轉(zhuǎn)化映射關(guān)系
  3. 寫(xiě)語(yǔ)法轉(zhuǎn)化代碼

本質(zhì)上這個(gè)程序就是一個(gè)翻譯器,將 vue-js語(yǔ)法翻譯成 vue-ts語(yǔ)法,難點(diǎn)在于你要找到二者之間所有語(yǔ)法的映射關(guān)系,并知道如何進(jìn)行處理,所以實(shí)際上大部分都是體力活

只要你明白了這其中的套路,其實(shí)換個(gè)什么 vue 轉(zhuǎn) wepy,或者 react轉(zhuǎn)微信小程序,其實(shí)都是一樣,都是翻譯器,都是體力活,只不過(guò)有些很輕松,也就是搬幾塊磚的事情,而有些體力活比較辛苦還需要?jiǎng)幽X子罷了

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

網(wǎng)頁(yè)題目:vue語(yǔ)法自動(dòng)轉(zhuǎn)typescript(解放雙手)
文章地址:http://www.muchs.cn/article30/jdoopo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google自適應(yīng)網(wǎng)站、企業(yè)網(wǎng)站制作、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、靜態(tài)網(wǎng)站、

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)