自尤大去年9月推出vue對typescript的支援後,一直想開箱嘗試,對於前端sr來說,vue的順滑加上ts的物件導向,想著就非常美好~ 終於在兩個月前,找到了個機會嘗試了一把vue+ts的組合。 開文記錄下vue和ts整合之旅和遇到的一些坑。
vue
應該大部分人都知道vue,畢竟如今vue是與react肩並肩的存在,所以就不過多介紹啦。
vue中文官網 官網上的教程就是最好的入門教程
typescript
我在前幾篇文章就一直有在宣傳typescript,簡單列舉下ts的優點
-
始於JavaScript,歸於JavaScript,typescript是JavaScript的超集,所以它可以重用JavaScriptd程式碼,使用JavaScript的庫
-
JavaScript用的優點它都有,跨瀏覽器、跨作業系統等
-
物件導向的程式設計思想,強大的型別檢查
-
開源大法好
要說缺點的話,那就是不太適合太小的專案。
就憑這些優點,足夠我們愉快的玩耍~
ts安裝
先將node安裝,然後在通過npm安裝ts的包
npm install -g typescript
tsc -v
檢視ts的版本號
建立專案
我們將通過官方提供的腳手架 vue-cli 來建立專案
安裝腳手架與建立專案
- 執行安裝命令
npm install -g @vue/cli
安裝完成後,可以通過 vue create
快速建立一個新專案的腳手架,或者直接通過 vue serve
構建新想法的原型。
- 建立專案
vue create vue-ts
vue-ts是我們的專案名稱,執行後如下
可看到有這麼幾個選項,xiaoli
這個選項是我之前建立的,稍後會介紹;default
這個後面寫著 babel eslint ,表示若選擇這個,那麼只會引入babel和 ealint;manually select features
顧名思義,選擇我們想要的。那麼我們選擇第三個
可看到列表裡有很多選項,我們以vue+ts為主,所以我們選擇 babel typescript router vuex
這幾個,選擇完後,如下
接下來會有好幾個yes or no 的選項,大家根據自己專案的需要來選擇就可以,最後一步,Save this as a preset for future projects?
若選擇yes,就會將我們之前的選擇儲存起來,作為一個預設選項,方便後續一鍵建立新專案。所有步驟選完,回車,便開始建立專案檔案結構和拉取npm包
專案結構
專案結構如下
public: 用於存放靜態檔案,index.html
入口檔案就放在裡面,這個資料夾下的檔案不會納入webpack的打包中;
src:存放vue專案工程檔案,其中已經幫我們關聯好router和vuex,檔案結構非常簡潔
其他:webpack、babel等配置檔案
與typescript服用
專案在構建中,已經引入 vue-class-component
,用於對ts的支援,或者使用 vue-property-decorator
,這個庫是在之前那個的基礎上擴充套件。
以下列舉tsvue寫法的各種變化
元件宣告
建立元件的方式變成如下
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
@Component
export default class Test extends Vue {
}
複製程式碼
data物件
通過建構函式建立data裡的資料
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
@Component
export default class Test extends Vue {
private name: string;
constructor() {
super();
this.name = 'xiaoli';
}
}
複製程式碼
data裡的資料使用方式如下
public getName(){
return this.name
}
複製程式碼
Prop宣告
@Prop() public msg: string;
@Prop({ default: 'default value' }) propB: string
@Prop([String, Boolean]) propC: string | boolean
複製程式碼
生命週期函式使用
public created(): void {
console.log('created');
}
public mounted():void{
console.log('mounted')
}
複製程式碼
自定義方法
js下是需要在method物件中宣告方法,現變成如下
public clickFunc(): void {
console.log(this.name)
console.log(this.msg)
}
複製程式碼
Watch監聽屬性
@Watch('name',{ immediate: true, deep: true })
public onChildChanged(val: string, oldVal: string) {
console.log('watch new name=' + val);
}
複製程式碼
computed計算屬性
public get allname() {
return 'computed ' + this.name;
}
複製程式碼
allname是計算後的值,name是被監聽的值
emit事件
@Emit()
addToCount(n: number) {
this.count += n
}
@Emit('reset')
resetCount() {
this.count = 0
}
複製程式碼
第一個的事件名稱為 add-to-count
,n
為傳過去的引數;第二個事件名為reset-count
,引數為空
指令和過濾器
----------小小更新一下---------------
有小夥伴問指令和過濾器在ts下的寫法,之前給遺漏了,現在補充一下。
我嘗試了下,發現之前在入口檔案直接引入指令或者過濾器的方式不管用了,因為用了ts後,元件的作用域跟之前的不一樣了,然後我找了官方的issue,截圖如下
在作者在4月11號回覆裡,承認了這個問題,但具體什麼時候將指令和過濾器的宣告加上就未知了,不過作者在issue中給出瞭解決方案。我寫個簡單的小栗子
一個自定義指令
// ./directive/index.ts
export const focus = {
// 當被繫結的元素插入到 DOM 中時……
inserted: function (el:HTMLElement) {
// 聚焦元素
el.focus()
}
}
複製程式碼
一個過濾器
// ./filter/index.ts
export const capitalize = function (value:string) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
複製程式碼
元件中使用
import { capitalize }from '@/filter/index'
import { focus } from '@/directive/index'
@Component({
filters:{capitalize},
directives:{focus}
})
export default class Test extends Vue {}
複製程式碼
<div>
<input v-focus v-model="modelData">
<div>{{modelData | capitalize}}</div>
</div>
複製程式碼
可看出這是區域性引用,全域性引用目前還沒找到辦法,歡迎有解決辦法的小夥伴指教~
vuex與ts的糅合
因為vuex是個可選的,所以單獨列出來。首先需要引用 vuex-class
庫,該庫有如下幾個模組
import {
namespace,
Action,
Getter,
Mutation,
State
} from 'vuex-class';
複製程式碼
分別對應vuex中的 action、getter、mutation等,使用ts對vuex的影響主要在元件對vuex的呼叫上,vuex的定義還是按照之前的寫法即可
@State('foo') stateFoo
@State(state => state.bar) stateBar
@Getter('foo') getterFoo
@Action('foo') actionFoo
@Mutation('foo') mutationFoo
@someModule.Getter('foo') moduleGetterFoo
// If the argument is omitted, use the property name
// for each state/getter/action/mutation type
@State foo
@Getter bar
@Action baz
@Mutation qux
複製程式碼
若不想使用vuex定義的方法名,可以自定義屬性名,因為都是定義在當前this上,所以直接使用this呼叫即可
this.getterFoo // -> store.getters.foo
this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
複製程式碼
小結
搭建完成後,就可以像後端一樣擼前端啦~