国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

詳解Vue2.5+遷移至Typescript指南

2019-11-19 11:05:42
字體:
來源:轉載
供稿:網友

為什么要遷移至Typescript

Javascript本身是動態弱類型的語言,這樣的特點導致了Javascript代碼中充斥著很多Uncaught TypeError的報錯,給開發調試和線上代碼穩定都帶來了不小的負面影響。

而Typescript提供了靜態類型檢查,使很多類型錯誤在編寫時就已經發現,不會帶到測試階段。

同時,Javascript不定義model就可以使用一個對象,有人喜歡這樣的靈活性,的確這樣的語法在model不復雜的時候可以快速的開發出需要的功能,但一旦model龐大,找一個需要的屬性值都不知道從何找起。而在Typescript中,我們需要使用TS中的interface type等方式先定義出model,才可以調用其屬性值,所以Typescript極大的提高了代碼的可讀性。

可行性

因為TypeScript是JavaScript的超集,TypeScript 不會阻止 JavaScript 的運行,即使存在類型錯誤也不例外,這能讓你的 JavaScript 逐步遷移至 TypeScript。所以可以慢慢地做遷移,一次遷移一個模塊,選擇一個模塊,重命名.js文件到.ts,在代碼中添加類型注釋。當你完成這個模塊時,選擇下一個模塊。

如何將已有的Vue項目遷移至Typescript

安裝依賴

Vue官方提供了一個庫Vue-class-component,用于讓我們使用Ts的類聲明方式來編寫vue組件代碼。Vue-property-decorator則是在Vue-class-component的基礎上提供了裝飾器的方式來編寫代碼。首先我們需要在package.json中引入這兩個依賴。

我的項目是基于vue-cli@3.X創建的,還需要在項目中引入@vue/cli-plugin-typescript typescript兩個依賴來完成Typescript的編譯。

配置tsconfig.json

在項目根目錄新建tsconfig.json,并引入以下代碼

{ "compilerOptions": {  "target": "esnext",  "module": "esnext",  "strict": true,  "jsx": "preserve",  "importHelpers": true,  "moduleResolution": "node",  "experimentalDecorators": true,  "esModuleInterop": true,  "allowSyntheticDefaultImports": true,  "sourceMap": true,  "baseUrl": ".",  "noFallthroughCasesInSwitch":true,  "noImplicitAny":true,  "noImplicitReturns":true,  "noImplicitThis":true,  "types": [   "webpack-env"  ],  "paths": {   "@/*": [    "./app/common/*"   ],   "_app/*": [    "./app/*"   ],   "_c/*": [    "./app/common/components/*"   ],   "api/*": [    "./app/service/*"   ],   "assets/*": [    "./app/assets/*"   ]  },  "lib": [   "esnext",   "dom",   "dom.iterable",   "scripthost"  ], }, "include": [   // 在此出填寫你的項目中需要按照typescript編譯的文件路徑  "app/**/*.ts",  "app/**/*.tsx",  "app/**/*.d.ts",  "app/**/*.vue", ], "exclude": [  "node_modules" ]}

特別需要注意的是,現在的vue項目中大多使用了webpack的alias來解析路徑,在tsconfig.json中需要配置path屬性,讓typescript同樣認識在webpack中配置的路徑別名。

添加全局聲明文件

因為在ts文件中是無法識別vue文件的,所以需要在項目根目錄新建shims-vue.d.ts文件,添加以下代碼,來讓ts識別vue文件。

import Vue from 'vue';declare module '*.vue' { export default Vue;}

由下而上的遷移

因為是遷移已經存在的項目,不建議開始就把main.js重命名為main.ts,對于絕大多數Vue項目,main.js引入了太多的依賴,我們應該首先從依賴著手,自下而上的遷移Typescript。對于項目中一些偏底層,甚至是框架維護者所提供的庫函數,我們不關心其實現邏輯,所以沒有必要將其改寫為ts文件,只需要給其加聲明文件供我們的業務代碼調用即可。

在我的項目中,service層的邏輯非常簡單,僅僅是傳參數調用接口,沒有添加任何其他的邏輯,邏輯如此簡單其實沒有什么必要改寫為ts文件,所以我為service層的文件編寫聲明文件,來為調用service層的代碼提供類型聲明。

聲明文件編寫方法

一個js文件如下

//service.jsimport axios from '@/libs/api.request'export default {  /**   * 創建賬戶   * @param {Object} data   * @param {String} data.accountType optinal   * @param {String} data.username   * @param {String} data.password   * @param {String} data.gender X | F | M   * @param {String} data.email   * @param {Number} data.level   */  createAccount(data) {    return axios.request({      url: `/api/account/createUser`,      method: 'post',      data: data    }).then((res) => [res, null]).catch((err) => [null, err]);  },}

可以看到,在使用typescript之前,對于一個函數的參數和返回值等信息的提示是通過jsdoc實現的,能夠在調用時確定參數類型及名稱,但jsdoc畢竟只是注釋,并不能提供類型校驗,所以在這里我們為其編寫聲明文件,編寫后的聲明文件如下

//service.d.tsinterface createAccountParams {  accountType?: string,  username: string,  password: string,  gender: 'X' | 'F' | 'M',  email: string,  level?: number}interface createAccountReturn {  userId: string,}export interface Service {  createAccount(data: createAccountParams): createAccountReturn}

這樣一個service層的接口文件的聲明文件就編寫完成了,為了獲得Typescript和vscode提供的類型提示和校驗,在main.js中將service.js導出的實例綁定在了Vue原型上,使得我們可以在vue組件中通過vm.$service方便的訪問service實例。但是Typescript并不知道Vue實例上有什么屬性,這時需要我們在之前添加的shims-vue.d.ts文件中添加幾行代碼。

import Vue from 'vue';import Service from "pathToService/service.d.ts";declare module '*.vue' { export default Vue;}declare module "vue/types/vue" { interface Vue {  $service: Service }}

得力于typescript中提供的模塊補充功能,我們可以在node_modules/vue/types/vue中補充我們需要在Vue上提供的屬性。

改寫Vue文件

我們需要將原來的vue文件改寫成使用vue-property-decorator編寫的方式。

<script lang="ts">import {Component,Vue} from "vue-property-decorator";@Componentexport default class MyComponent extends Vue{  // 聲明data  form: {    accountType?: string,    username: string,    password: string,    gender: 'X' | 'F' | 'M',    email: string,    level?: number  } = {    username:'',    password:'',    gender:'X',    email:''  };  // 聲明methods  async submit(): void {    //調用上面的service接口    const [res,err] = await this.$service.createAccount(this.form);  }}</script>

至此一個Vue項目遷移至Typescript的過程就已經完成了,剩下的工作就是將代碼中其他的文件一步步由js遷移到typescript中。

把方法綁定到Vue實例下

除了我們之前提到過的將自己編寫的service掛載到vue實例上,大家一定清楚在vue項目中,我們經常會調用this.$refs this.$router this.$store等等,typescript也會檢查這些屬性是否被綁定在了vue實例上,那么我們并沒有在類型系統中聲明這些值,按道理應該報Property '$refs' does not exist on type [your_component_name]
真相是vue vue-router vuex都已經給我們聲明了相應的類型,我們可以cd ./node_modules/vue/types/目錄中去查看
截取少量代碼如下所示:

export interface Vue { readonly $el: Element; readonly $options: ComponentOptions<Vue>; readonly $parent: Vue; readonly $root: Vue; readonly $children: Vue[]; readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] }; readonly $slots: { [key: string]: VNode[] | undefined }; readonly $scopedSlots: { [key: string]: NormalizedScopedSlot | undefined }; readonly $isServer: boolean; readonly $data: Record<string, any>; readonly $props: Record<string, any>; readonly $ssrContext: any; readonly $vnode: VNode; readonly $attrs: Record<string, string>; readonly $listeners: Record<string, Function | Function[]>;}

只要正常的在依賴中安裝了vue-router vuex就已經通過模塊補充的方式將類型添加到了vue實例上。

在一些項目中,vue-router vuex這些依賴不是通過安裝在依賴中引入的,而是通過index.html引入的cdn資源文件,這樣在開發過程中我們就無法獲取其類型。

這個時候我們可以通過安裝@types依賴的方式將類型系統補充到項目中,如npm install @types/jquery --save-dev。

不幸的是vue-router和vuex的types包已經廢棄了,只能通過手動去github上下載對應版本的vue-router vuex將types文件引入到項目中,你可以像我一樣在項目中新建一個types目錄,引入需要的類型聲明文件。

這樣就可以直接在vue實例上訪問到$store $router等屬性了。

同理當你想要引入其他的組件庫上的一些類型文件時,也是這樣的方式。

一些需要注意的問題

在vue開發過程中我們會使用this.$refs去訪問某一個具體實例的方法,但是這在ts中是訪問不到的常見的,比如要想要使用form組件中的validate方法,我們需要給其加上類型斷言
this.$refs.form.validate()變為(this.$refs.form as Vue & {validate:Function}).validate()
來告訴編譯器this.$refs.form上有validate方法。

因為類型斷言前提條件是是當 S 類型是 T 類型的子集,或者 T 類型是 S 類型的子集時,S 能被成功斷言成 T,所以需要在類型斷言時合并Vue類型。

同時也可以通過vue-property-decorator提供給我們的裝飾器一勞永逸的將該refs添加到computed屬性上

import { Vue, Component, Ref } from 'vue-property-decorator'import Form from '@/path/to/another-component.vue'@Componentexport default class YourComponent extends Vue { @Ref() readonly form!: Form}

等同于

export default { computed() {  form: {   cache: false,   get() {    return this.$refs.form as Form   }  }, }}

這樣我們就可以通過 this.form.validate()來做表單校驗了

新手容易遇到的一些問題

疑問1:interface和type有什么區別?

type 可以聲明基本類型別名,聯合類型,元組等類型

eg.type a = string;是被允許的,

interface 會自動聲明合并

interface person{  gender:string  age:number}interface person{  name:string}

疑問2: 錯誤 Property 'hideContent' has no initializer and is not definitely assigned in the constructor.

strictPropertyInitialization屬性會在strict設置為true時自動被設置為true。

但這個屬性并不合理,它要求每個實例的屬性都有初始值,我們在tsconfig將其設置為false就好了。

疑問3: 賦值兼容性

interface person {  name:string  age:number}interface student {  name:string  age:number  stuid:string}let person: person= {  name:'name',  age:1}let student: student = {  name:'name',  age:1,  stuid:'stuid'};person = student //這樣是可以的student = person //這樣不允許

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 中西区| 密山市| 汶川县| 泗水县| 祥云县| 静宁县| 沛县| 大新县| 英超| 鸡西市| 耿马| 太和县| 万年县| 惠州市| 郑州市| 湘阴县| 周宁县| 广宗县| 永胜县| 南平市| 双流县| 丰镇市| 崇阳县| 玛多县| 渑池县| 西城区| 铜山县| 乌兰察布市| 兰西县| 渝中区| 抚顺县| 南澳县| 扶沟县| 永春县| 东乌珠穆沁旗| 宽城| 文水县| 许昌市| 桂林市| 萨迦县| 隆林|