初次在Vue項目使用TypeScript,需要做什麼

来源:https://www.cnblogs.com/chanwahfung/archive/2019/12/14/11968205.html
-Advertisement-
Play Games

前言 總所周知,Vue新版本3.0 使用 TypeScript 開發,讓本來就很火的 TypeScript 受到更多人的關註。雖然 TypeScript 在近幾年才火,但其實它誕生於2012年10月,正式版本發佈於2013年6月,是由微軟編寫的自由和開源的編程語言。TypeScript 是 Java ...


前言

總所周知,Vue新版本3.0 使用 TypeScript 開發,讓本來就很火的 TypeScript 受到更多人的關註。雖然 TypeScript 在近幾年才火,但其實它誕生於2012年10月,正式版本發佈於2013年6月,是由微軟編寫的自由和開源的編程語言。TypeScript 是 JavaScript 的一個超集,擴展了 JavaScript 的語法,添加了可選的靜態類型和基於類的面向對象編程。

JavaScript開發中經常遇到的錯誤就是變數或屬性不存在,然而這些都是低級錯誤,而靜態類型檢查恰好可以彌補這個缺點。什麼是靜態類型?舉個慄子:

//javascript 
let str = 'hello'
str = 100 //ok

//typescript
let str:string = 'hello'
str = 100 //error: Type '100' is not assignable to type 'string'.

可以看到 TypeScript 在聲明變數時需要為變數添加類型,如果變數值和類型不一致則會拋出警告。靜態類型只在編譯時進行檢查,而且最終編譯出來的代碼依然是 JavaScript。即使我們為 string 類型的變數賦值為其他類型,代碼也是可以正常運行的。

其次,TypeScript 增加了代碼的可讀性和可維護性,類型定義實際上就是一個很好的文檔,比如在使用函數時,只需要看看參數和返回值的類型定義,就大概知道這個函數如何工作。

目錄

準備工作

npm

安裝 typescript

npm install typescript @vue/cli-plugin-typescript -D

新增文件

在項目的根目錄下創建 shims-vue.d.ts、shims-tsx.d.ts、tsconfig.json

  • shims-vue.d.ts
import Vue from 'vue';

declare module '*.vue' {
  export default Vue;
}
  • shims-tsx.d.ts
import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    type Element = VNode
    type ElementClass = Vue
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}
  • tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators":true,
    "sourceMap": true,
    "noImplicitThis": false,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

ESLint配置

為什麼使用 ESLint 而不是 TSLint?

今年1月份,TypeScript官方發佈博客推薦使用ESLint來代替TSLint。而 ESLint 團隊將不再維護 typescript-eslint-parser,也不會在 Npm 上發佈,任何使用 tyescript-eslint-parser 的用戶應該改用 @tyescript-eslint/parser

官方的解釋:

我們註意到TSLint規則的操作方式存在一些影響性能的體繫結構問題,ESLint已經擁有了我們希望從linter中得到的更高性能的體繫結構。此外,不同的用戶社區通常有針對ESLint而不是TSLint構建的lint規則(例如React hook或Vue的規則)。鑒於此,我們的編輯團隊將專註於利用ESLint,而不是複製工作。對於ESLint目前沒有覆蓋的場景(例如語義linting或程式範圍的linting),我們將致力於將ESLint的TypeScript支持與TSLint等同起來。

原文

如何使用

AlloyTeam 提供了一套全面的EsLint配置規範,適用於 React/Vue/Typescript 項目,並且可以在此基礎上自定義規則。
GitHub

安裝

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-alloy

配置項的說明查看AlloyTeam ESLint 規則

配置

在項目的根目錄中創建.eslintrc.js,然後將以下內容複製到其中:

module.exports = {
  extends: [
    'alloy',
    'alloy/typescript',
  ],
  env: {
    browser: true,
    node: true,
  },
  rules: {
    // 自定義規則
    'spaced-comment': 'off',
    '@typescript-eslint/explicit-member-accessibility': 'off',
    'grouped-accessor-pairs': 'off',
    'no-constructor-return': 'off',
    'no-dupe-else-if': 'off',
    'no-import-assign': 'off',
    'no-setter-return': 'off',
    'prefer-regex-literals': 'off'
  }
};

補充

如果想知道配置項更多使用,可以到ESLint官網搜索配置項。

如果使用的是VScode,推薦使用ESLint插件輔助開發。

文件改造

入口文件

  1. main.js 改為 main.ts
  2. vue.config.js 修改入口文件
const path = require('path')
module.exports = {
  ...
  pages: {
    index: {
      entry: path.resolve(__dirname+'/src/main.ts')
    },
  },
  ...
}

vue組件文件

隨著TypeScript和ES6里引入了類,在一些場景下我們需要額外的特性來支持標註或修改類及其成員。 裝飾器(Decorators)為我們在類的聲明及成員上通過元編程語法添加標註提供了一種方式。

Vue 也為我們提供了類風格組件的 TypeScript 裝飾器,使用裝飾器前需要在 tsconfig.json 將 experimentalDecorators 設置為 true。

安裝vue裝飾器

vue-property-decorator庫完全依賴vue-class-component,在安裝時要一起裝上

npm install vue-class-component vue-property-decorator -D

改造.vue

只需要修改srcipt內的東西即可,其他不需要改動

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import draggable from 'vuedraggable'

@Component({
  created(){
    
  },
  components:{
    draggable
  }
})
export default class MyComponent extends Vue {
  /* data */
  private ButtonGrounp:Array<any> = ['edit', 'del']
  public dialogFormVisible:boolean = false
  
  /*method*/
  setDialogFormVisible(){
    this.dialogFormVisible = false
  }
  addButton(btn:string){
    this.ButtonGrounp.push(btn)
  }

  /*compute*/
  get routeType(){
    return this.$route.params.type
  }
}
</script>

類成員修飾符,不添加修飾符則預設為public

  • public:公有,可以自由訪問類的成員
  • protected:保護,類及其繼承的子類可訪問
  • private:私有,只有類可以訪問

Prop

!: 為屬性使用明確的賦值斷言修飾符,瞭解更多看文檔

import { Component, Vue, Prop } from "vue-property-decorator";
export default class MyComponent extends Vue {
  ...
  @Prop({type: Number,default: 0}) readonly id!: number
  ...
}

等同於

export default {
  ...
  props:{
    id:{
      type: Number,
      default: 0
    }
  }
  ...
}

Watch

import { Component, Vue, Watch } from "vue-property-decorator";
export default class MyComponent extends Vue {
  ...
  @Watch('dialogFormVisible')
  dialogFormVisibleChange(newVal:boolean){
    // 一些操作
  }
  ...
}

等同於

export default {
  ...
  watch:{
    dialogFormVisible(){
      // 一些操作
    }
  }
  ...
}

Provide/Inject

// App.vue
import {Component, Vue, Provide} from 'vue-property-decorator'
@Component
export default class App extends Vue {
  @Provide() app = this
}

// MyComponent.vue
import {Component, Vue, Inject} from 'vue-property-decorator'
@Component
export default class MyComponent extends Vue {
  @Inject() readonly app!: Vue
}

等同於

// App.vue
export default {
  provide() {
    return {
      'app': this
    }
  }
}

// MyComponent.vue
export default {
  inject: ['app']
}

更多裝飾器使用,參考vue-property-decorator文檔

全局聲明

*.d.ts 文件

目前主流的庫文件都是 JavaScript 編寫,TypeScript 身為 JavaScript 的超集,為支持這些庫的類型定義,提供了類型定義文件(*.d.ts),開發者編寫類型定義文件發佈到npm上,當使用者需要在 TypeScript 項目中使用該庫時,可以另外下載這個包,讓JS庫能夠在 TypeScript 項目中運行。

比如:md5 相信很多人都使用過,這個庫可以將字元串轉為一串哈希值,這種轉化不可逆,常用於敏感信息進行哈希再發送到後端進行驗證,保證數據安全性。如果我們想要在 TypeScript 項目中使用,還需要另外下載 @tyeps/md5,在該文件夾的index.d.ts中可以看到為 md5 定義的類型。

/// <reference types="node" />

declare function md5(message: string | Buffer | Array<number>): string;

declare namespace md5 {}

export = md5;

TypeScript 是如何識別 *.d.ts

TypeScript 在項目編譯時會全局自動識別.d.ts文件,我們需要做的就是編寫.d.ts,然後 TypeScript 會將這些編寫的類型定義註入到全局提供使用。

為vue實例添加屬性/方法

當我們在使用this.$route或一些原型上的方法時,typescript無法進行推斷,在編譯時會報屬性$route不存在的錯誤,需要為這些全局的屬性或方法添加全局聲明

對shims-vue.d.ts做修改,當然你也可以選擇自定義*.d.ts來添加聲明

import Vue from 'vue';
import VueRouter, { Route } from 'vue-router'

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

declare module 'vue/types/vue' {
  interface Vue {
    $api: any;
    $bus: any;
    $router: VueRouter;
    $route: Route;
  }
}

自定義類型定義文件

當一些類型或介面等需要頻繁使用時,我們可以為項目編寫全局類型定義,
根路徑下創建@types文件夾,裡面存放*.d.ts文件,專門用於管理項目中的類型定義文件。

這裡我定義個global.d.ts文件:

//declare 可以創建 *.d.ts 文件中的變數,declare 只能作用域最外層
//變數
declare var num: number;

//類型
type StrOrNum = string | number

//函數
declare function handler(str: string): void;

// 類
declare class User { 
  
}

//介面
interface OBJ {
  [propName: string]: any;
  [propName: number]: any;
}

interface RES extends OBJ {
  resultCode: number;
  data: any;
  msg?: string;
}

解放雙手,transvue2ts轉換工具

改造過程最麻煩的就是語法轉換,內容都是一些固定的寫法,這些重覆且枯燥的工作可以交給機器去做。這裡我們可以藉助 transvue2ts 工具提高效率,transvue2ts 會幫我們把data、prop、watch等語法轉換為裝飾器語法。

安裝

npm i transvue2ts -g

使用

安裝完之後,transvue2ts 庫的路徑會寫到系統的 path中,直接打開命令行工具即可使用,命令的第二個參數是文件的完整路徑。
執行命令後會在同級目錄生成轉換好的新文件,例如處理view文件夾下的index.vue,轉換後會生成indexTS.vue。

處理單文件組件

transvue2ts D:\typescript-vue-admin-demo\src\pages\index.vue
=>
輸出路徑:D:\typescript-vue-admin-demo\src\pages\indexTS.vue

處理文件夾下的所有vue組件文件

transvue2ts D:\typescript-vue-admin-demo\src\pages
=>
輸出路徑:D:\typescript-vue-admin-demo\src\pagesTS

補充

不要以為有工具真就完全解放雙手,工具只是幫我們轉換部分語法。工具未能處理的語法和參數的類型定義,還是需要我們去修改的。要註意的是轉換後註釋會被過濾掉。

該工具作者在掘金對工具的介紹和實現思路

關於第三方庫使用

一些三方庫會在安裝時,包含有類型定義文件,使用時無需自己去定義,可以直接使用官方提供的類型定義。

node_modules中找到對應的包文件夾,類型文件一般都會存放在types文件夾內,其實類型定義文件就像文檔一樣,這些內容能夠清晰的看到所需參數和參數類型。

這裡列出一些在 Vue 中使用三方庫的例子:

element-ui 組件參數

使用類型定義

import { Component, Vue } from "vue-property-decorator";
import { ElLoadingComponent, LoadingServiceOptions } from 'element-ui/types/loading'

let loadingMark:ElLoadingComponent; 
let loadingConfig:LoadingServiceOptions = {
  lock: true,
  text: "載入中",
  spinner: "el-icon-loading",
  background: "rgba(255, 255, 255, 0.7)"
};

@Component
export default class MyComponent extends Vue {
  ...
  getList() {
    loadingMark = this.$loading(loadingConfig);
    this.$api.getList()
      .then((res:RES) => {
        loadingMark.close();
      });
  }
  ...
}

element-ui/types/loading,原文件里還有很多註釋,對每個屬性都做出描述

export interface LoadingServiceOptions {
  target?: HTMLElement | string
  body?: boolean
  fullscreen?: boolean
  lock?: boolean
  text?: string
  spinner?: string
  background?: string
  customClass?: string
}
export declare class ElLoadingComponent extends Vue {
  close (): void
}
declare module 'vue/types/vue' {
  interface Vue {
    $loading (options: LoadingServiceOptions): ElLoadingComponent
  }
}

vue-router 鉤子函數

使用類型定義

import { Component, Vue } from "vue-property-decorator";
import { NavigationGuard } from "vue-router";

@Component
export default class MyComponent extends Vue {
  beforeRouteUpdate:NavigationGuard = function(to, from, next) {
    next();
  }
}

在vue-router/types/router.d.ts中,開頭就可以看到鉤子函數的類型定義。

export type NavigationGuard<V extends Vue = Vue> = (
  to: Route,
  from: Route,
  next: (to?: RawLocation | false | ((vm: V) => any) | void) => void
) => any

還有前面所使用到的RouterRoute,所有的方法、屬性、參數等都在這裡被描述得清清楚楚

export declare class VueRouter {
  constructor (options?: RouterOptions);

  app: Vue;
  mode: RouterMode;
  currentRoute: Route;

  beforeEach (guard: NavigationGuard): Function;
  beforeResolve (guard: NavigationGuard): Function;
  afterEach (hook: (to: Route, from: Route) => any): Function;
  push (location: RawLocation, onComplete?: Function, onAbort?: ErrorHandler): void;
  replace (location: RawLocation, onComplete?: Function, onAbort?: ErrorHandler): void;
  go (n: number): void;
  back (): void;
  forward (): void;
  getMatchedComponents (to?: RawLocation | Route): Component[];
  onReady (cb: Function, errorCb?: ErrorHandler): void;
  onError (cb: ErrorHandler): void;
  addRoutes (routes: RouteConfig[]): void;
  resolve (to: RawLocation, current?: Route, append?: boolean): {
    location: Location;
    route: Route;
    href: string;
    normalizedTo: Location;
    resolved: Route;
  };

  static install: PluginFunction<never>;
}
export interface Route {
  path: string;
  name?: string;
  hash: string;
  query: Dictionary<string | (string | null)[]>;
  params: Dictionary<string>;
  fullPath: string;
  matched: RouteRecord[];
  redirectedFrom?: string;
  meta?: any;
}

自定義三方庫聲明

當使用的三方庫未帶有 *.d.ts 聲明文件時,在項目編譯時會報這樣的錯誤:

 Could not find a declaration file for module 'vuedraggable'. 'D:/typescript-vue-admin-demo/node_modules/vuedraggable/dist/vuedraggable.umd.min.js' implicitly has an 'any' type.
  Try `npm install @types/vuedraggable` if it exists or add a new declaration (.d.ts) file containing `declare module 'vuedraggable';`

大致意思為 vuedraggable 找不到聲明文件,可以嘗試安裝 @types/vuedraggable(如果存在),或者自定義新的聲明文件。

安裝 @types/vuedraggable

按照提示先選擇第一種方式,安裝 @types/vuedraggable,然後發現錯誤 404 not found,說明這個包不存在。感覺這個組件還挺多人用的(周下載量18w),沒想到社區居然沒有聲明文件。

自定義聲明文件

無奈只能選擇第二種方式,說實話自己也摸索了有點時間(主要對這方面沒做多瞭解,不太熟悉)

首先在 node_modules/@types 下創建 vuedraggable 文件夾,如果沒有 @types 文件夾可自行創建。vuedraggable 文件夾下創建 index.d.ts。編寫以下內容:

import Vue from 'vue'
declare class Vuedraggable extends Vue{}
export = Vuedraggable

重新編譯後沒有報錯,解決問題。

建議及註意事項

改造過程

  • 在接入 TypeScript 時,不必一次性將所有文件都改為ts語法,原有的語法也是可以正常運行的,最好就是單個修改
  • 初次改造時出現一大串的錯誤是正常的,基本上都是類型錯誤,按照錯誤提示去翻譯進行修改對應錯誤
  • 在導入ts文件時,不需要加 .ts 尾碼
  • 為項目定義全局變數後無法正常使用,重新跑一遍伺服器(我就碰到過...)

遇到問題

  • 面向搜索引擎,前提是知道問題出在哪裡
  • 多看仔細文檔,大多數一些錯誤都是比較基礎的,文檔可以解決問題
  • Github 找 TypeScript 相關項目,看看別人是如何寫的

寫在最後

抽著空閑時間入門一波 TypeScript,嘗試把一個後臺管理系統接入 TypeScript,畢竟只有實戰才能知道有哪些不足,以上記錄都是在 Vue 中如何使用 TypeScript,以及遇到的問題。目前工作中還未正式使用到 TypeScript,學習新技術需要成本和時間,大多數是一些中大型的公司在推崇。總而言之,多學點總是好事,學習都要多看多練,知道得越多思維就會更開闊,解決問題的思路也就越多。

參考資料


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、簡介 開源流式處理系統在不斷地發展,從一開始只關註低延遲指標到現在兼顧延遲、吞吐與結果準確性,在發展過程中解決了很多問題,編程API的易用性也在不斷地提高。本文介紹一下 Flink 中的核心概念,這些概念是學習與使用 Flink 十分重要的基礎知識,在後續開發 Flink 程式過程中將會幫助開發 ...
  • Elasticsearch OOM 優化 改文件類型及segments force merge ...
  • 當連接sql server本地伺服器 報一下錯誤: 標題: 連接到伺服器 無法連接到 localhost。 。。。。。 打開windows服務管理界面(win+R鍵,) 接著找到 再次連接即可 ...
  • 元類, pymysql 一、元類 自定義元類 二、pymysql 通過pymysql 去連接資料庫 ...
  • https://sqlserver.code.blog/2019/12/14/connect-sql-server-from-linux-client-using-windows-authentication-and-troubleshoot-steps/ ...
  • 裝載請標明出處,維權必究:https://www.cnblogs.com/tangZH/p/12040927.html 首先我們在as中雙擊apk,出現apk的分析界面,然後選中保存簽名相關文件的文件夾: 一:簽名文件: 1、MANIFEST.MF:保存了所有其他文件的SHA-1並base64編碼後 ...
  • 轉載請標明出處,維權必究:https://www.cnblogs.com/tangZH/p/12037179.html 繼上次這一篇後:https://www.cnblogs.com/tangZH/p/12013685.html 我繼續探討,這次的場景和上一次的場景是一樣的,在一個佈局中,根佈局是相 ...
  • 題目描述: 給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和為目標值的那 兩個 整數,並返回他們的數組下標。 你可以假設每種輸入只會對應一個答案。但是,你不能重覆利用這個數組中同樣的元素。 示例: 給定 nums = [2, 7, 11, 15], target = 9 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...