typescript vuex_Vue3+TypeScript完整项目上手教程
作者:TinssonTai
https://juejin.im/post/6875713523968802829
一個完整的Vue3+Ts項目,支持.vue和.tsx寫法
項目地址:https://github.com/vincentzyc/vue3-demo.git
TypeScript 是JS的一個超集,主要提供了類型系統和對ES6的支持,使用 TypeScript 可以增加代碼的可讀性和可維護性,在 react 和 vue 社區中也越來越多人開始使用TypeScript。從最近發布的 Vue3 正式版本來看, Vue3 的源碼就是用 TypeScript 編寫的,更好的 TypeScript 支持也是這一次升級的亮點。當然,在實際開發中如何正確擁抱 TypeScript 也是遷移至 Vue3 的一個小痛點,這里就針對 Vue3 和 TypeScript 展開一些交流。
96.8%的代碼都是TypeScript,支持的力度也是相當大?
- Vue3入口: https://github.com/vuejs/vue-next
項目搭建
在官方倉庫的 Quickstart 中推薦用兩種方式方式來構建我們的 SPA 項目:
- vite
- vue-cli
vue?create?sail-vue3
#?select?vue?3?preset
vite 是一個由原生ESM驅動的Web開發構建工具,打開 vite 依賴的 package.json 可以發現在 devDependencies 開發依賴里面已經引入了TypeScript ,甚至還有 vuex , vue-router , less , sass 這些本地開發經常需要用到的工具。vite 輕量,開箱即用的特點,滿足了大部分開發場景的需求,作為快速啟動本地 Vue 項目來說,這是一個非常完美的工具。
后面的演示代碼也是用vite搭的
從 vue2.x 走過來的掘友肯定知道 vue-cli 這個官方腳手架, vue3 的更新怎么能少得了 vue-cli 呢, vue-cli 更強調的是用 cli 的方式進行交互式的配置,選擇起來更加靈活可控。豐富的官方插件適配,GUI的創建管理界面,標準化開發流程,這些都是 vue-cli 的特點。
- vue-cli ? TypeScript STEP1
- vue-cli ? TypeScript STEP2
想要預裝TypeScript,就需要選擇手動配置,并check好TypeScript
忘記使用選擇 TypeScript 也沒事,加一行cli命令就行了
vue?add?typescript最后,別忘了在 .vue 代碼中,給 script 標簽加上 lang="ts"
<script?lang="ts">Option API風格
在 Vue2.x 使用過 TypeScript 的掘友肯定知道引入 TypeScript 不是一件簡單的事情:
然后出來的代碼風格是這樣的:
@Component({????components:{?componentA,?componentB},
})
export?default?class?Parent?extends?Vue{
??@Prop(Number)?readonly?propA!:?number?|?undefined
??@Prop({?default:?'default?value'?})?readonly?propB!:?string
??@Prop([String,?Boolean])?readonly?propC!:?string?|?boolean?|?undefined
??//?data信息
??message?=?'Vue2?code?style'
??//?計算屬性
??private?get?reversedMessage?():?string[]?{
??????return?this.message.split('?').reverse().join('')
??}
??//?method
??public?changeMessage?():?void?{
????this.message?=?'Good?bye'
??}
}
class 風格的組件,各種裝飾器穿插在代碼中,有點感覺自己不是在寫 vue ,些許凌亂?,所以這種曲線救國的方案在 vue3 里面肯定是行不通的。
在 vue3 中可以直接這么寫:
import?{?defineComponent,?PropType?}?from?'vue'interface?Student?{
??name:?string
??class:?stringage:?number
}const?Component?=?defineComponent({
??props:?{
????success:?{?type:?String?},
????callback:?{
??????type:?Function?as?PropType<()?=>?void>
????},
????student:?{
??????type:?Object?as?PropType,required:?true
????}
??},
??data()?{return?{message:?'Vue3?code?style'
????}
??},computed:?{
????reversedMessage():?string?{return?this.message.split('?').reverse().join('')
????}
??}
})
vue 對 props 進行復雜類型驗證的時候,就直接用 PropType 進行強制轉換, data 中返回的數據也能在不顯式定義類型的時候推斷出大多類型, computed 也只用返回類型的計算屬性即可,代碼清晰,邏輯簡單,同時也保證了 vue 結構的完整性。
Composition API風格
在 vue3 的 Composition API 代碼風格中,比較有代表性的api就是 ref 和 reactive ,我們看看這兩個是如何做類型聲明的:
ref
import?{?defineComponent,?ref?}?from?'vue'const?Component?=?defineComponent({
setup()?{
??const?year?=?ref(2020)
??const?month?=?ref('9')
??month.value?=?9?//?OKconst?result?=?year.value.split('')?//?=>?Property?'split'?does?not?exist?on?type?'number'
?}
})
分析上面的代碼,可以發現如果我們不給定 ref 定義的類型的話, vue3 也能根據初始值來進行類型推導,然后需要指定復雜類型的時候簡單傳遞一個泛型即可。
Tips:如果只有setup方法的話,可以直接在defineComponent中傳入setup函數
const?Component?=?defineComponent(()?=>?{????const?year?=?ref(2020)
????const?month?=?ref('9')
????month.value?=?9?//?OKconst?result?=?year.value.split('')?//?=>?Property?'split'?does?not?exist?on?type?'number'
})
reactive
import?{?defineComponent,?reactive?}?from?'vue'interface?Student?{
??name:?string
??class?:?stringage:?number
}export?default?defineComponent({
??name:?'HelloWorld',
??setup()?{
????const?student?=?reactive({?name:?'阿勇',?age:?16?})//?orconst?student:?Student?=?reactive({?name:?'阿勇',?age:?16?})//?orconst?student?=?reactive({?name:?'阿勇',?age:?16,?class:?'cs'?})?as?Student
??}
})
聲明 reactive 的時候就很推薦使用接口了,然后怎么使用類型斷言就有很多種選擇了,這是 TypeScript 的語法糖,本質上都是一樣的。
自定義Hooks
vue3 借鑒 react hooks 開發出了 Composition API ,那么也就意味著 Composition API 也能進行自定義封裝 hooks ,接下來我們就用 TypeScript 風格封裝一個計數器邏輯的 hooks ( useCount ):
首先來看看這個 hooks 怎么使用:
import?{?ref?}?from?'/@modules/vue'import??useCount?from?'./useCount'
export?default?{
??name:?'CountDemo',
??props:?{
????msg:?String
??},
??setup()?{
????const?{?current:?count,?inc,?dec,?set,?reset?}?=?useCount(2,?{
??????min:?1,
??????max:?15
????})
????const?msg?=?ref('Demo?useCount')
????return?{
??????count,
??????inc,
??????dec,
??????set,
??????reset,
??????msg
????}
??}
}
出來的效果就是:
貼上 useCount 的源碼:
import?{?ref,?Ref,?watch?}?from?'vue'interface?Range?{
??min?:?number,
??max?:?number
}
interface?Result?{
??current:?Ref,inc:?(delta?:?number)?=>?void,dec:?(delta?:?number)?=>?void,set:?(value:?number)?=>?void,reset:?()?=>?void
}export?default?function?useCount(initialVal:?number,?range?:?Range):?Result?{const?current?=?ref(initialVal)const?inc?=?(delta?:?number):?void?=>?{if?(typeof?delta?===?'number')?{
??????current.value?+=?delta
????}?else?{
??????current.value?+=?1
????}
??}const?dec?=?(delta?:?number):?void?=>?{if?(typeof?delta?===?'number')?{
??????current.value?-=?delta
????}?else?{
??????current.value?-=?1
????}
??}const?set?=?(value:?number):?void?=>?{
????current.value?=?value
??}const?reset?=?()?=>?{
????current.value?=?initialVal
??}
??watch(current,?(newVal:?number,?oldVal:?number)?=>?{if?(newVal?===?oldVal)?returnif?(range?&&?range.min?&&?newVal???????current.value?=?range.min
????}?else?if?(range?&&?range.max?&&?newVal?>?range.max)?{
??????current.value?=?range.max
????}
??})return?{
????current,
????inc,
????dec,set,
????reset
??}
}
分析源碼
這里首先是對 hooks 函數的入參類型和返回類型進行了定義,入參的 Range 和返回的 Result 分別用一個接口來指定,這樣做了以后,最大的好處就是在使用 useCount 函數的時候,ide就會自動提示哪些參數是必填項,各個參數的類型是什么,防止業務邏輯出錯。
接下來,在增加 inc 和減少 dec 的兩個函數中增加了 typeo 類型守衛檢查,因為傳入的 delta 類型值在某些特定場景下不是很確定,比如在 template 中調用方法的話,類型檢查可能會失效,傳入的類型就是一個原生的 Event 。
關于 ref 類型值,這里并沒有特別聲明類型,因為 vue3 會進行自動類型推導,但如果是復雜類型的話可以采用類型斷言的方式:ref(initObj) as Ref
小建議 ?
AnyScript
在初期使用 TypeScript 的時候,很多掘友都很喜歡使用 any 類型,硬生生把TypeScript 寫成了 AnyScript ,雖然使用起來很方便,但是這就失去了 TypeScript 的類型檢查意義了,當然寫類型的習慣是需要慢慢去養成的,不用急于一時。
Vetur
vetur 代碼檢查工具在寫vue代碼的時候會非常有用,就像構建 vue 項目少不了 vue-cli 一樣,vetur 提供了 vscode 的插件支持,趕著升級 vue3 這一波工作,順帶也把 vetur 也帶上吧。
一個完整的Vue3+ts項目
├─public│??????favicon.ico
│??????index.html
└─src
????│??App.vue
????│??main.ts
????│??shims-vue.d.ts
????├─assets
????│??│??logo.png
????│??└─css
????│??????????base.css
????│??????????main.styl
????├─components
????│??│??HelloWorld.vue
????│??└─base
????│??????????Button.vue
????│??????????index.ts
????│??????????Select.vue
????├─directive
????│??????focus.ts
????│??????index.ts
????│??????pin.ts
????├─router
????│??????index.ts
????├─store
????│??????index.ts
????├─utils
????│??│??cookie.ts
????│??│??deep-clone.ts
????│??│??index.ts
????│??│??storage.ts
????│??└─validate
????│??????????date.ts
????│??????????email.ts
????│??????????mobile.ts
????│??????????number.ts
????│??????????system.ts
????└─views
????????│??About.vue
????????│??Home.vue
????????│??LuckDraw.vue
????????│??TodoList.vue
????????└─address
????????????????AddressEdit.tsx
????????????????AddressList.tsx
- .vue寫法
??...
???...
- tsx寫法
import?{?AddressList,?NavBar,?Toast,?Popup?}?from?"vant";
import?AddressEdit?from?'./AddressEdit'
import?router?from?'@/router'
export?default?{
??setup()?{
????const?chosenAddressId?=?ref('1')
????const?showEdit?=?ref(false)
????const?list?=?reactive([
??????{
????????id:?'1',
????????name:?'張三',
????????tel:?'13000000000',
????????address:?'浙江省杭州市西湖區文三路?138?號東方通信大廈?7?樓?501?室',
????????isDefault:?true,
??????},
??????{
????????id:?'2',
????????name:?'李四',
????????tel:?'1310000000',
????????address:?'浙江省杭州市拱墅區莫干山路?50?號',
??????},
????])
????const?disabledList?=?reactive([
??????{
????????id:?'3',
????????name:?'王五',
????????tel:?'1320000000',
????????address:?'浙江省杭州市濱江區江南大道?15?號',
??????},
????])
????const?onAdd?=?()?=>?{
??????showEdit.value?=?true
????}
????const?onEdit?=?(item:?any,?index:?string)?=>?{
??????Toast('編輯地址:'?+?index);
????}
????const?onClickLeft?=?()?=>?{
??????router.back()
????}
????const?onClickRight?=?()?=>?{
??????router.push('/todoList')
????}
????return?()?=>?{
??????return?(
????????????????????
????????????left-text="返回"
????????????right-text="Todo"
????????????left-arrow
????????????onClick-left={onClickLeft}
????????????onClick-right={onClickRight}
??????????/>????????????vModel={chosenAddressId.value}
????????????list={list}
????????????disabledList={disabledList}
????????????disabledText="以下地址超出配送范圍"
????????????defaultTagText="默認"
????????????onAdd={onAdd}
????????????onEdit={onEdit}
??????????/>
??????);
????};
??}
};
結束
不知不覺, Vue 都到3的One Piece時代了, Vue3 的新特性讓擁抱 TypeScript 的姿勢更加從容優雅, Vue 面向大型項目開發也更加有底氣了,點擊查看更多。
點個『在看』支持下總結
以上是生活随笔為你收集整理的typescript vuex_Vue3+TypeScript完整项目上手教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gaussian09使用教程linux,
- 下一篇: vue render函数_Vue原理解析