Vue項目中使用
前面兩篇介紹過TypeScript基礎和較深入的東西,本章介紹如何在Vue項目中使用。
項目創建
創建項目直接使用Vue-cli創建
下面是步驟:
1.運行vuecli,
2.選擇合適的目錄創建項目
3.輸入項目名並,選擇包管理器,輸入git倉庫初始化內容
4.設置預設,如果你之前有合適的預設,可以設置該預設,這里選擇手動
5.選擇功能,其中TypeScript和babel必選,其他功能視項目而定:
6.設置配置,開啟類樣式組件語法(第一項),選擇eslint配置為ESLint+Standard(第五項),開啟保存時檢查和修復代碼
7.創建項目
8.安裝一些插件
編輯器文件支持設置
這里以我們以后統一使用的Webstrom為例:
找到Editor>File and COde Templates
新建一個代碼模板,輸入名稱,擴展名為vue,選擇根據樣式重新設置格式、選擇啟用該模板
內容區域輸入:
<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>
點擊應用之后,新建文件時選擇剛建立的模板:
因為選擇的是類樣式語法,所以需要填入類名:
修改eslint、編輯器配置
修改eslint配置文件以支持檢查TypeScript文件
.eslintrc.js文件的parserOptions選項中新增如下代碼:
parser: '@typescript-eslint/parser'
修改編輯器配置以支持保存時自動修復代碼
webstrom強大之處是對各種技術的支持,比如eslint檢查代碼。正常情況下,我們需要通過命令行檢查代碼,這非常麻煩,不過webstrom能夠配置eslint,編輯器內檢查不合eslint配置的代碼,並且支持保存時修復。配置如下:
修改聲明文件
對於我們自定義的插件、全局方法、全局變量等,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; }; } }
對於其他內容和上面方法類似
組件中使用
基本用法
因為使用類樣式寫組件中的js,所以寫法上會略有不同,但是本質上還是不變的。
組件引用
組件引用通過傳遞參數components
給裝飾符@Component
@Component({ components: { MapLegend: () => import('@/components/legend/index.vue') } })
過濾器
過濾器和組件類似,通過傳遞filters
參數,filters
對象內定義局部過濾器:
@Component({ filters: { dateFormat (date:Date) { return dateFormat(date) } } })
指令
局部指令和過濾器類似,通過@Component
裝飾器傳遞參數directives
:
@Component({ directives:{ focus: { inserted: function (el) { el.focus() } } } })
props
props不再通過對象屬性形式定義,而是通過@Prop
裝飾器定義,其配置內容通過參數形式傳入裝飾器:
@Component export default class Test extends Vue { @Prop({ type:String, default:'no name', required:false }) name!:string mounted(){ console.log(name) } }
props同步版
通過@PropSync可以創建一個props屬性的同步版本(即:變量改變,所對應的props屬性也會隨之改變):
@Component export default class Test extends Vue { @PropSync('name',{ type:String, default:'no name', required:false }) name!:string mounted(){ this.name='nichols' } }
偵聽器(watch)
類似的,偵聽器通過@Watch
裝飾器定義,裝飾器接收兩個參數,第一個監視哪個變量,第二個其他配置:
@Component export default class Test extends Vue { isShow=false @Watch('isShow',{ deep:true, immediate:true }) onIsShowChange(val:boolean,newVal:boolean){ } }
偵聽器同樣可以被當做方法調用,只是執行其內部邏輯:
mounted(){ this.onIsShowChange(true,false) }
emit
在組件中觸發事件讓父組件偵聽到是一個非常常用的操作,通過@Emit
裝飾符定義,所定義的函數可以被當做普通函數調用:
@Component export default class Test extends Vue { name = '' @Emit('change') onChange (name: string) { this.name = name } mounted () { this.onChange('nichols') } }
其中如果有返回值,則返回值會作為觸發的參數放在前面,而傳入參數會放到返回值后面
ref
定義ref使用@Ref裝飾器定義:
@Component export default class Test extends Vue { name = '' @Ref('form') form!:HTMLElement mounted(){ console.log(this.form.offsetHeight) } }
data
對於組件內的數據,我們可以直接使用類屬性定義組件的數據:
@Component export default class Test extends Vue { isShow = false form: { username: string; password: string; } = { username: '', password: '' } mounted () { this.isShow = true } }
函數(methods)
函數與data
定義類似,為類添加一個方法即可:
@Component export default class Test extends Vue { log(){ // .... } }
計算屬性
而計算屬性,則是類的存取器的寫法(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
完成:
@Component export default class Test extends Vue { mounted () { } created () { } updated () { } beforeDestroy () { } destroyed () { } }
更加詳細的內容
類型聲明
src
目錄下types
目錄下,創建index.d.ts(或者更詳細的文件名),然后定義類型,這里以擴展Event為例
interface Event{ DataTransfer:{ setData():void } }
為了避免全局變量混亂,可以使用export
導出我們想要被外部訪問的聲明:
export interface User{ id:string; name:string; realName:string; }
需要使用時,再在需要使用的地方導入即可:
import { User } from '@/types/user' @Component export default class Test extends Vue { user:User={ id: '', name: '', realName: '' } }
舊項目的遷移
安裝插件
1.啟動vue ui(一把梭,就是干!),在插件項中點擊添加插件,
2.搜索TypeScript,選擇@vue/cli-pluging-typescript
,點擊安裝即可
修改組件
1.script標簽添加屬性lang="ts"
2.組件引入添加.vue
后綴名
3.修改默認導出為類樣式:
export default { name:'Component1' }
修改為:
@Component export default class Component1 extends Vue{ }
4.按照基本用法,將對應的數據更改為類樣式
5.按照編輯器報錯提示添加或者修改類型注釋
修改js文件
1.js文件后綴改為.ts
2.添加類型約束
vuex的使用
vuex和vue組件使用方式類似,使用類樣式+裝飾器的形式定義,使用的依賴是vuex-module-decorators
和vuex-class
安裝
yarn add vuex-module-decorators vuex-class npm i vuex-module-decorators vuex-class
創建Store
Store的創建和常規創建方式一致,只是Vuex.Store參數中無需傳入任何參數:
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({ })
定義模塊
@Module
使用@Module
定義一個vuex模塊,接收如下參數:
屬性 |
數據類型 |
描述 |
name |
string |
模塊的名稱(如果有名稱空間) |
namespaced |
boolean |
模塊是否具有名稱空間 |
stateFactory |
boolean |
是否開啟狀態工廠(方便模塊復用) |
dynamic |
true |
如果這是一個動態模塊(在創建存儲后添加到存儲)
|
store |
Store<any> |
將注入此模塊的存儲區(用於動態模塊) |
preserveState |
boolean |
如果啟用此選項,則加載模塊時將保留狀態 |
創建模塊語法如下:
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的定義和組件中的data定義類似:
@Module({ dynamic: true,//啟用動態模塊 name: 'User', store,//注入store namespaced: true, stateFactory: true//開啟工廠模式 }) export default class User extends VuexModule { token = getToken() }
上面代碼和下面代碼效果一樣:
export default { state:{ token: getToken() }, namespaced:true }
@Mutation
mutation的定義使用@Mutation
裝飾器定義:
@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,該裝飾器接收三個參數:
參數名 |
類型 |
描述 |
commit |
string |
所提交的荷載 |
rawError |
boolean |
是否打印原始錯誤類型(默認會對報錯信息進行包裝) |
root |
boolean |
是否允許提交根荷載 |
如果不傳入參數,需要手動提交荷載:
@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('/') } }
如果指定提交的荷載名,可通過函數的返回值設定荷載值:
@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合二為一,用此裝飾器定義的action會同時定義並提交荷載:
@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') } }
組件中使用
組件中通過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 { } }
小程序中使用
小程序中使用TypeScript比較簡單,在創建項目時選擇語言為TypeScript,其他的和Vue類似項目類似
不同的是,Vue項目會自動編譯TypeScript,而小程序需要手動編譯ts文件,這顯得有點麻煩,所以我們可以使用webstrom開發小程序:
安裝小程序插件,讓webstrom支持小程序語法:File>Setting>Plugins,搜索Wechat mini programs upport
,完成之后重啟webstrom,這時候我們可以看到在右鍵菜單New那一項里面多了兩個小程序選項:
配置ts文件自動編譯:File>Setting>Languages & Frameworks>TypeScript,選中改變時重新編譯:
擴展
本系列文章全面結合項目編寫,如果你還想深入學習TypeScript的話可以看下面的網站:
如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。