工(gong)作記錄:TypeScript從入門到項目(mu)實戰(項目(mu)篇)
Vue項目中使用(yong)
前面(mian)兩篇介紹(shao)過TypeScript基(ji)礎(chu)和較深(shen)入的東西(xi),本章介紹(shao)如何在Vue項(xiang)目中使用。
項目創建
創建項目直(zhi)接(jie)使(shi)用(yong)Vue-cli創建

下面是步驟:
1.運行(xing)vuecli,
2.選擇合適的目錄創建項(xiang)目
3.輸入項目名(ming)并,選擇包(bao)管理(li)器,輸入git倉庫(ku)初(chu)始(shi)化內(nei)容

4.設(she)置(zhi)預設(she),如(ru)果你之前(qian)有合適的(de)預設(she),可以設(she)置(zhi)該(gai)預設(she),這里(li)選擇手動
5.選(xuan)擇功能,其中TypeScript和babel必(bi)選(xuan),其他功能視項目而定:

6.設(she)置(zhi)配(pei)(pei)置(zhi),開(kai)啟(qi)類樣式組件語(yu)法(第一項(xiang)),選(xuan)擇(ze)eslint配(pei)(pei)置(zhi)為ESLint+Standard(第五項(xiang)),開(kai)啟(qi)保存(cun)時檢(jian)查和修(xiu)復(fu)代碼

7.創建項(xiang)目
8.安裝一些插件
編輯器文件支持設置
這里(li)以我(wo)們(men)以后(hou)統(tong)一使用(yong)的Webstrom為例:
找到Editor>File and COde Templates

新建(jian)一個(ge)代碼模(mo)板(ban),輸(shu)入名稱,擴(kuo)展名為vue,選(xuan)擇(ze)根據樣式重(zhong)新設置格式、選(xuan)擇(ze)啟(qi)用該模(mo)板(ban)

內容(rong)區域輸入:
<template lang="pug">
#[[$END$]]#
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class ${COMPONENT_NAME} extends Vue {
}
</script>
<style lang="stylus" rel="stylesheet/stylus" module>
</style>
點擊應用之后,新(xin)建文(wen)件時(shi)選(xuan)擇剛建立的模板(ban):

因(yin)為選(xuan)擇的是類樣式語法,所以需要(yao)填入(ru)類名:

修改eslint、編輯器配置
修改eslint配置文件以(yi)支(zhi)持檢查TypeScript文件(jian)
.eslintrc.js文件的parserOptions選項中新增如下(xia)代(dai)碼:
parser: '@typescript-eslint/parser'
修改編輯器配置以支(zhi)持保(bao)存(cun)時自動修復代碼
webstrom強(qiang)大(da)之處是對各種技術的(de)支持,比如eslint檢查(cha)代(dai)碼(ma)。正(zheng)常情況下,我們(men)需要通過命令(ling)行檢查(cha)代(dai)碼(ma),這(zhe)非常麻煩,不(bu)過webstrom能夠配(pei)置eslint,編輯器內(nei)檢查(cha)不(bu)合eslint配(pei)置的(de)代(dai)碼(ma),并(bing)且支持保存(cun)時(shi)修復。配(pei)置如下:

修(xiu)改(gai)聲(sheng)明(ming)文件
對于我們自定義的插件、全局方法、全局變量等,TypeScript并不知道他們,為了讓TypeScript認識他們,我們可以通過聲明文件告訴TypeScript,如果使用model樣式時的$style,我們修改shims-tsx.d.ts文件,在行末添加下面代碼:
declare module 'vue/types/vue' {
interface Vue {
$style: {
[key: string]: string;
};
}
interface VueConstructor {
$style: {
[key: string]: string;
};
}
}
對于(yu)其他(ta)內容和上面方法類似
組件(jian)中(zhong)使用(yong)
基本(ben)用法(fa)
因為使用類樣(yang)式寫組件中的js,所(suo)以寫法(fa)上(shang)會略有(you)不同,但是本(ben)質上(shang)還是不變的。
組件引用
組件引用通過傳遞參數components給裝飾符@Component
@Component({
components: {
MapLegend: () => import('@/components/legend/index.vue')
}
})
過濾(lv)器
過濾器和組件類似,通過傳遞filters參數,filters對(dui)象內定義(yi)局(ju)部過(guo)濾(lv)器:
@Component({
filters: {
dateFormat (date:Date) {
return dateFormat(date)
}
}
})
指(zhi)令(ling)
局部指令和過濾器類似,通過@Component裝飾器傳遞參數directives:
@Component({
directives:{
focus: {
inserted: function (el) {
el.focus()
}
}
}
})
props
props不再通過對象屬性形式定義,而是通過@Prop裝飾(shi)器(qi)定義(yi),其配(pei)置(zhi)內容通過參數(shu)形(xing)式傳入裝飾(shi)器(qi):
@Component
export default class Test extends Vue {
@Prop({
type:String,
default:'no name',
required:false
}) name!:string
mounted(){
console.log(name)
}
}
props同步版
通過@PropSync可以創建(jian)一(yi)個props屬(shu)性的同步版本(即:變(bian)量改變(bian),所(suo)對應的props屬(shu)性也會(hui)隨之改變(bian)):
@Component
export default class Test extends Vue {
@PropSync('name',{
type:String,
default:'no name',
required:false
}) name!:string
mounted(){
this.name='nichols'
}
}
偵聽器(watch)
類似的,偵聽器通過@Watch裝飾器定(ding)義,裝飾器接收兩個(ge)(ge)參(can)數,第一個(ge)(ge)監視(shi)哪個(ge)(ge)變量,第二個(ge)(ge)其他配置(zhi):
@Component
export default class Test extends Vue {
isShow=false
@Watch('isShow',{
deep:true,
immediate:true
})
onIsShowChange(val:boolean,newVal:boolean){
}
}
偵(zhen)聽器同樣可以被當做方(fang)法調用,只是(shi)執(zhi)行其(qi)內部邏(luo)輯:
mounted(){
this.onIsShowChange(true,false)
}
emit
在組件中觸發事件讓父組件偵聽到是一個非常常用的操作,通過@Emit裝飾符(fu)定(ding)義,所定(ding)義的函數可以被當做普(pu)通函數調用:
@Component
export default class Test extends Vue {
name = ''
@Emit('change')
onChange (name: string) {
this.name = name
}
mounted () {
this.onChange('nichols')
}
}
其(qi)中如果有(you)返(fan)回值(zhi)(zhi),則返(fan)回值(zhi)(zhi)會作(zuo)為觸發的參數(shu)放在前面(mian),而(er)傳入(ru)參數(shu)會放到(dao)返(fan)回值(zhi)(zhi)后面(mian)
ref
定義ref使用@Ref裝飾器定義:
@Component
export default class Test extends Vue {
name = ''
@Ref('form') form!:HTMLElement
mounted(){
console.log(this.form.offsetHeight)
}
}
data
對于組件內的(de)數(shu)據(ju),我們可以直接(jie)使(shi)用(yong)類屬性定義(yi)組件的(de)數(shu)據(ju):
@Component
export default class Test extends Vue {
isShow = false
form: {
username: string;
password: string;
} = {
username: '',
password: ''
}
mounted () {
this.isShow = true
}
}
函數(methods)
函數與data定義類似,為(wei)類添(tian)加一個方(fang)法(fa)即可:
@Component
export default class Test extends Vue {
log(){
// ....
}
}
計算屬(shu)性
而計算屬性,則是類的存取器的寫法(getter、setter,對應Vue的getter和setter):
@Component
export default class Test extends Vue {
lastName = '尼古拉斯'
firstName = '趙四'
get name (): string {
return `${this.firstName}·${this.lastName}`
}
set name (name: string) {
const names = name.split('·')
this.firstName = names[0]
this.lastName = names[0]
}
}
生命周期
可以直接定義所對應的鉤子名稱,或者借助vue-class-component/hooks.d.ts完(wan)成:
@Component
export default class Test extends Vue {
mounted () {
}
created () {
}
updated () {
}
beforeDestroy () {
}
destroyed () {
}
}
更(geng)加(jia)詳細的內(nei)容
更詳細參考
類(lei)型聲明
src目錄下types目錄下,創建index.d.ts(或者更詳細的文件名(ming)),然后定義類型,這里(li)以擴展Event為例
interface Event{
DataTransfer:{
setData():void
}
}
為了避免全局變量混亂,可以使用export導出我(wo)們想(xiang)要被(bei)外部訪(fang)問的聲(sheng)明:
export interface User{
id:string;
name:string;
realName:string;
}
需(xu)要(yao)使(shi)(shi)用時,再在(zai)需(xu)要(yao)使(shi)(shi)用的地方導入即可:
import { User } from '@/types/user'
@Component
export default class Test extends Vue {
user:User={
id: '',
name: '',
realName: ''
}
}
舊項(xiang)目的(de)遷移
安裝插件(jian)
1.啟動(dong)vue ui(一把梭(suo),就是干!),在插(cha)件項(xiang)中點擊添加插(cha)件,

2.搜索TypeScript,選擇@vue/cli-pluging-typescript,點擊安裝即可(ke)
修(xiu)改組(zu)件
1.script標簽添加屬性lang="ts"
2.組件引入添加.vue后綴名
3.修改默認導(dao)出為類(lei)樣式:
export default {
name:'Component1'
}
修改為:
@Component
export default class Component1 extends Vue{
}
4.按照基本用法,將對應(ying)的數據(ju)更改為(wei)類樣式
5.按照編輯器報錯提(ti)示(shi)添加(jia)或(huo)者修改類(lei)型(xing)注(zhu)釋
修改(gai)js文(wen)件
1.js文件后綴改為.ts
2.添加類型約束(shu)
vuex的使用
vuex和vue組件使用方式類似,使用類樣式+裝飾器的形式定義,使用的依賴是vuex-module-decorators和vuex-class
安裝
yarn add vuex-module-decorators vuex-class npm i vuex-module-decorators vuex-class
創建Store
Store的創建(jian)和常規創建(jian)方式一(yi)致,只(zhi)是Vuex.Store參數中無需傳(chuan)入任何(he)參數:
import Vue from 'vue'
import Vuex from 'vuex'
import User from '@/store/modules/user'
import getters from '@/store/getters'
Vue.use(Vuex)
export default new Vuex.Store({
})
定義(yi)模塊
@Module
使用@Module定(ding)義一(yi)個vuex模塊(kuai),接收如下參(can)數:
|
屬性 |
數據類(lei)型 |
描述 |
|
name |
string |
模塊(kuai)的名稱(如果有名稱空(kong)間) |
|
namespaced |
boolean |
模塊是否具有名稱空間 |
|
stateFactory |
boolean |
是否開(kai)啟狀態(tai)工廠(方便(bian)模塊復(fu)用) |
|
dynamic |
true |
如果(guo)這是一個動(dong)態(tai)模塊(在創建存(cun)儲后(hou)添加到存(cun)儲)
|
|
store |
Store<any> |
將(jiang)注入此(ci)模(mo)塊的存(cun)儲區(用于(yu)動態模(mo)塊) |
|
preserveState |
boolean |
如果啟(qi)用此選項,則(ze)加(jia)載模塊(kuai)時將保留狀態(tai) |
創建(jian)模塊語(yu)法如下:
import { VuexModule } from 'vuex-module-decorators'
@Module({
dynamic: true,//啟用動態模塊
name: 'User',
store,//注入store
namespaced: true,
stateFactory: true//開啟工廠模式
})
export default class User extends VuexModule {
}
state
state的定義(yi)和組(zu)件中的data定義(yi)類似:
@Module({
dynamic: true,//啟用動態模塊
name: 'User',
store,//注入store
namespaced: true,
stateFactory: true//開啟工廠模式
})
export default class User extends VuexModule {
token = getToken()
}
上面(mian)代碼和(he)下面(mian)代碼效(xiao)果一樣:
export default {
state:{
token: getToken()
},
namespaced:true
}
@Mutation
mutation的定義使用@Mutation裝(zhuang)飾器定(ding)義:
@Module({
dynamic: true,//啟用動態模塊
name: 'User',
store,//注入store
namespaced: true,
stateFactory: true//開啟工廠模式
})
export default class User extends VuexModule {
token = getToken()
@Mutation
setToken (token: string) {
this.token = token
token ? setToken(token) : deleteToken()
}
}
@Action
使用@Action裝飾器定義action,該(gai)裝飾器接收三個參(can)數(shu):
|
參數名 |
類型 |
描述 |
|
commit |
string |
所提交的荷載 |
|
rawError |
boolean |
是否打印原始(shi)錯誤類型(默認會對報錯信息進(jin)行包裝(zhuang)) |
|
root |
boolean |
是否允許提交根荷載 |
如(ru)果不(bu)傳入參數,需要手(shou)動提交(jiao)荷載:
@Module({
dynamic: true,//啟用動態模塊
name: 'User',
store,//注入store
namespaced: true,
stateFactory: true//開啟工廠模式
})
export default class User extends VuexModule {
token = getToken()
@Mutation
setToken (token: string) {
this.token = token
token ? setToken(token) : deleteToken()
}
@Action
async login () {
this.context.commit('setToken', 'token')
router.replace('/')
}
}
如果指定提交(jiao)的荷載名,可通過函數的返(fan)回值(zhi)(zhi)設(she)定荷載值(zhi)(zhi):
@Module({
dynamic: true,//啟用動態模塊
name: 'User',
store,//注入store
namespaced: true,
stateFactory: true//開啟工廠模式
})
export default class User extends VuexModule {
token = getToken()
@Mutation
setToken (token: string) {
this.token = token
token ? setToken(token) : deleteToken()
}
@Action({commit:'setToken'})
async login ():string {
router.replace('/')
return 'token'
}
}
@MutationAction
有時候簡單地數據操作,mutation會顯得有點多余,這時候,我們可以使用@MutationAction裝飾器將mutatioin和action合二為(wei)一,用(yong)此裝飾器定義(yi)的action會同時定義(yi)并(bing)提交荷載:
@Module({
dynamic: true,//啟用動態模塊
name: 'User',
store,//注入store
namespaced: true,
stateFactory: true//開啟工廠模式
})
export default class User extends VuexModule {
testStr = ''
@MutationAction({ mutate: ['testStr'] })
async setStr () {
return new Promise<{ testStr: string }>(resolve => {
resolve({
testStr: 'test'
})
})
}
}
需要注意的是:返回對象的數據結構必須和指定的參數名一致
getter
getter的定義和Vue組件中的計算屬性定義類似,使用get前置于方法名:
@Module
class MyModule extends VuexModule {
wheels = 2
get axles() {
return this.wheels / 2
}
}
完整示例
import {
deleteRefreshToken,
deleteToken,
deleteUserInfo,
getRefreshToken,
getToken,
getUserInfo,
setRefreshToken,
setToken,
setUserInfo
} from '@/utils/auth'
import { UserInfo } from '@/types/user'
import router from '@/router'
import { Action, Module, Mutation, MutationAction, VuexModule } from 'vuex-module-decorators'
@Module({
dynamic: true,//啟用動態模塊,模塊將在調用getModule時注冊
name: 'User',
store,//注入store
namespaced: true,
stateFactory: true//開啟工廠模式
})
export default class User extends VuexModule {
token = getToken()
refreshToken = getRefreshToken()
userInfo: UserInfo | null = getUserInfo()
testStr = ''
@Mutation
setToken (token: string) {
this.token = token
token ? setToken(token) : deleteToken()
}
@Mutation
setRefreshToken (token: string) {
this.refreshToken = token
token ? setRefreshToken(token) : deleteRefreshToken()
}
@Mutation
setUserInfo (user: UserInfo | null) {
this.userInfo = user
user ? setUserInfo(user) : deleteUserInfo()
}
@MutationAction({ mutate: ['testStr'] })
async setStr () {
return new Promise<{ testStr: string }>(resolve => {
resolve({
testStr: 'test'
})
})
}
@Action
async login () {
this.context.commit('setToken', 'token')
this.context.commit('setRefreshToken', 'refreshToken')
this.context.commit('setUserInfo', {})
router.replace('/')
}
@Action
async loginOut () {
this.context.commit('setToken', '')
this.context.commit('setRefreshToken', '')
this.context.commit('setUserInfo', null)
router.replace('/login')
}
}
組(zu)件中使用
組件中通過getModule()方法進行獲取到模塊,可以通過定義計算屬性以使用state:
import { Component, Vue } from 'vue-property-decorator'
import User from '@/store/modules/user'
import { getModule } from 'vuex-module-decorators'
import { namespace } from 'vuex-class'
let user:User = getModule(User)
@Component
export default class test extends Vue {
//動態數據,使用user.userInfo獲取的并不是響應式數據
@namespace('User').State('userInfo') userInfo: UserInfo
login ():void {
console.log(user.testStr)
user.login()
}
get token ():string {
return user.token
}
mounted ():void {
}
}
小程序中使用(yong)
小程(cheng)序中使用(yong)TypeScript比(bi)較簡單,在創(chuang)建(jian)項目時選擇語言為TypeScript,其他的和Vue類似項目類似

不(bu)同的是(shi),Vue項目會自動編(bian)譯(yi)TypeScript,而(er)小程(cheng)序(xu)需要手動編(bian)譯(yi)ts文件,這顯得有點(dian)麻煩,所以我們可以使用(yong)webstrom開發(fa)小程(cheng)序(xu):
安裝小程序插件,讓webstrom支持小程序語法:File>Setting>Plugins,搜索Wechat mini programs upport,完成之后重(zhong)啟webstrom,這時候(hou)我(wo)們可以看到在(zai)右鍵菜單(dan)New那(nei)一項里面多了(le)兩個小程序選項:

配置ts文件自動編譯:File>Setting>Languages & Frameworks>TypeScript,選中改變時重(zhong)新編譯:

擴展
本系列文章全面結合項目(mu)編寫,如果(guo)你還想深入學習TypeScript的(de)話(hua)可以(yi)看下面的(de)網站:
如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

