前言
總所周知,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插件輔助開發,
檔案改造
入口檔案
- main.js 改為 main.ts
- 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(newVal){
// 一些操作
}
}
...
}
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
還有前面所使用到的Router、Route,所有的方法、屬性、引數等都在這里被描述得清清楚楚
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;
one rror (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,學習新技術需要成本和時間,大多數是一些中大型的公司在推崇,總而言之,多學點總是好事,學習都要多看多練,知道得越多思維就會更開闊,解決問題的思路也就越多,
參考資料
- TypeScript入門教程
- 技術胖-TypeScript免費視頻教程
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/162725.html
標籤:JavaScript
上一篇:案例——洗掉添加記錄
下一篇:基于Postman中的報錯
