1. 程式人生 > 其它 >ts專案例項_TypeScript+Vue專案開發入門

ts專案例項_TypeScript+Vue專案開發入門

技術標籤:ts專案例項typescript vuexvue el-tree 預設選中vue ts 設定tslint提示vue中既可以選擇又可以手動輸入的文字框型別vue中選擇彈出提示框 可選擇yes 或no

TypeScript是國內外前端技術圈被評為2020年最受歡迎的技術之一,如果你還沒開始學,是不是就out了呢?今天我們就開始TypeScript+Vue專案開發的探索,帶你體驗完全不一樣的Vue開發方式。話不多說,讓我們開始吧......

1、初始化專案及環境搭建

1.1、全域性安裝vue腳手架

npminstall-g@vue/cli

目前預設安裝的是vue/cli的最新4.0版本,可使用如下命令檢視:

vue--version

1.2、使用[email protected] 4.0初始化專案

vuecreatets-vue-music
  • 選擇配置模式(自動/手動)
946809a58a0f7998ac7b29e982a7e75e.png

預設是自動,我們選擇手動模式

  • 選擇整合配置項
a5d6786282657dec91c37b227612f277.png

使用上下方向鍵選擇,空格鍵選中/取消

?Checkthefeaturesneededforyourproject:
(*)Babel是否開啟babel編譯
(*)TypeScript是否整合TS
()ProgressiveWebApp(PWA)Support是否支援PWA
(*)Router是否整合vue-router
(*)Vuex是否整合vuex
(*)CSSPre-processors是否使用css預處理
(*)Linter/Formatter是否規範程式碼型別
()UnitTesting是否使用單元測試
()E2ETesting是否使用E2E測試
  • 繼續選擇配置項
be82aabc8e489bd501986891a5c63b00.png

//是否使用class風格的元件語法
?Useclass-stylecomponentsyntax?Yes
//是否使用babel做轉義
?UseBabelalongsideTypeScript(requiredformodernmode,auto-detectedpolyfills,transpilingJSX)?Yes
//是否使用路由history模式
?Usehistorymodeforrouter?(Requiresproperserversetupforindexfallbackinproduction)Yes
//選擇css預處理型別,我們選擇node-sass
?PickaCSSpre-processor(PostCSS,AutoprefixerandCSSModulesaresupportedbydefault):Sass/SCSS(withnode-sass)
//選擇程式碼規範校驗工具,我們選擇ESLint+Prettier
?Pickalinter/formatterconfig:Prettier
//選擇儲存時校驗
?Pickadditionallintfeatures:(Presstoselect,totoggleall,toinvertselection)Lintonsave
//選擇Babel,ESLint,etc.等配置的儲存位置,我們選package.json檔案
?WheredoyoupreferplacingconfigforBabel,ESLint,etc.?Inpackage.json
//選擇是否儲存這些配置到以後專案中
?Savethisasapresetforfutureprojects?Yes

選擇完畢後就開始拉取配置,生成初始化專案檔案。

  • 啟動專案
cdts-vue-todolist//進入專案根目錄
npmrunserve//執行專案

專案啟動後,在瀏覽器輸入對應的地址就可以看到介面了。

1.3、改造專案結構

使用腳手架初始化後會預設生成一個專案結構目錄,但我們可以根據自己的專案需求進行改造。

  • 調整專案結構目錄
|——public入口html檔案
|——src原始檔目錄
|——apis請求api
|——assets靜態資源
|——components公共元件
|——directives自定義指令
|——filters過濾器
|——mixinsmixin混入
|——routervue-router路由
|——storevuex狀態管理
|——styles樣式
|——types型別宣告
|——utils工具方法
|——views頁面元件
|——App.vue入口頁面
|——main.ts入口檔案
|——shims-tsx.d.tstsx宣告檔案
|——shims-vue.d.tsvue宣告檔案
|——.gitignoregit忽略檔案配置
|——babel.config.jsbabel配置
|——package.json依賴配置
|——README.md專案readme檔案
|——tsconfig.jsonts配置
|——vue.config.jswebpack配置
  • 新增vue.config.js配置檔案

vue-cli腳手架預設生成的專案是零webpack配置的,但是零配置功能比較弱,@vue-cli支援自定義webpack配置,在根目錄新建vue.config.js檔案,這個檔案會被@vue/cli-service 自動載入。常用配置如下:

constpath=require("path");
constsourceMap=process.env.NODE_ENV==="development";

module.exports={
//基本路徑
publicPath:"./",
//輸出檔案目錄
outputDir:"dist",
//eslint-loader是否在儲存的時候檢查
lintOnSave:false,
//webpack配置
//參考https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
chainWebpack:()=>{},
configureWebpack:config=>{
if(process.env.NODE_ENV==="production"){
//為生產環境修改配置
config.mode="production";
}else{
//為開發環境修改配置
config.mode="development";
}

Object.assign(config,{
//開發生產共同配置
resolve:{
extensions:[".js",".vue",".json",".ts",".tsx"],
alias:{
vue$:"vue/dist/vue.js",
"@":path.resolve(__dirname,"./src"),
"@c":path.resolve(__dirname,"./src/components")
}
}
});
},
//生產環境是否生成sourceMap檔案
productionSourceMap:sourceMap,
//css相關配置
css:{
//是否使用css分離外掛ExtractTextPlugin
extract:true,
//開啟CSSsourcemaps?
sourceMap:false,
//css預設器配置項
loaderOptions:{},
//設定為false後你就可以去掉檔名中的.module並將所有的*.(css|scss|sass|less|styl(us)?)
requireModuleExtension:false
},
//usethread-loaderforbabel&TSinproductionbuild
//enabledbydefaultifthemachinehasmorethan1cores
parallel:require("os").cpus().length>1,
//PWA外掛相關配置
//seehttps://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
pwa:{},
//webpack-dev-server相關配置
devServer:{
open:true,//啟動後自動開啟瀏覽器
host:"localhost",
port:9002,,
https:false,
hotOnly:false,
proxy:{
//設定代理
//proxyallrequestsstartingwith/apitojsonplaceholder
"/api":{
target:"http://localhost:3000/",
changeOrigin:true,
ws:true,
pathRewrite:{
"^/api":""
}
}
},
before:app=>{}
},
//第三方外掛配置
pluginOptions:{
//...
}
};

至此,一套完整的Vue+TypeScript的開發環境就搭建完成了。接下來就可以愉快的進行專案開發了。

2、快速上手專案開發

2.1、讓TS識別Vue

  • .vue字尾檔案匯入TypeScript開發環境預設是隻能識別*.ts*.tsx檔案的,因此在遇到匯入*.vue檔案時,會無法識別,因此在匯入vue檔案時需要加上字尾.vue,如下:
importComponentfrom'components/component.vue'
  • 宣告檔案VueCli腳手架生成的專案目錄下預設會有一個shims-vue.d.ts檔案,內容如下:
declaremodule'*.vue'{
importVuefrom'vue'
exportdefaultVue
}

該宣告檔案是告訴TS以.vue為字尾的檔案交給Vue模組處理,檔案中vue是指Vue的例項。

2.2、用類的方式編寫元件

vue-class-component

vue-class-componentVue的官方一個基於類元件的一個庫,它可以讓我們以類的方式開發Vue元件,它提供的Component裝飾器為類添加註釋,從而以直觀和標準的類語法定義元件資料和方法。以下為Vue類元件的定義方式:

<template>
<inputv-model="name"@click='handleInputChange'>
template>
<script>importVuefrom'vue'importComponentfrom'vue-class-component'importchildComponentfrom'@/components/childComponent.vue'//註冊第三方庫鉤子函式,以Vue-Router為例,實際開發可以單獨抽成一個檔案,在main.ts中引入
Component.registerHooks(['beforeRouteEnter'
])//定義類元件,需要使用@Component裝飾器裝飾
@Component({//components、props、watch及其他options//其他options可以檢視:
components:{
childComponent
},props:{},watch:{}
})exportdefaultclassmyComponentextendsVue{//data
privatefirstName:string='Jim'
privatelastName:string='Green'//lifecyclehook
created(){}//路由元件內守衛鉤子函式
beforeRouteEnter(to,from,next){console.log('beforeRouteEnter')
next()
}//computedgetname(){//取值returnthis.firstName+''+this.lastName
}setname(value){//存值constsplitted=value.split('')this.firstName=splitted[0]this.lastName=splitted[1]||''
}//method
privatehandleInputChange():void{
}
}script>
vue-property-decorator

vue-property-decorator是第三方基於vue-class-component擴充套件而成的一個庫,它完全依賴vue-class-component,除了具備vue-class-component的能力以外,還另外擴充套件了10個裝飾器,分別為:@Prop@PropSync@Model@Watch@Provide@Inject@ProvideReactive@InjectReactive@Emit@Ref。以下詳細的解析下常用的幾個:

  • @Prop

@Prop裝飾器主要用來進行父元件向子元件傳遞資料

import{Vue,Component,Prop}from'vue-property-decorator'
@Component
exportdefaultclassYourComponentextendsVue{
@Prop(Number)readonlypropA:number|undefined
@Prop({default:'defaultvalue'})readonlypropB!:string
@Prop([String,Boolean])readonlypropC:string|boolean|undefined
}

等同於

exportdefault{
props:{
propA:{
type:Number
},
propB:{
default:'defaultvalue'
},
propC:{
type:[String,Boolean]
}
}
}
  • @Emit

@Emit裝飾器是用來子元件通過派發事件向父元件傳遞資料

import{Vue,Component,Emit}from'vue-property-decorator'
@Component
exportdefaultclassYourComponentextendsVue{
count=0
@Emit()
addToCount(n:number){
this.count+=n
}

@Emit('reset')
resetCount(){
this.count=0
}

@Emit()
returnValue(){
return10
}

@Emit()
onInputChange(e){
returne.target.value
}

@Emit()
promise(){
returnnewPromise(resolve=>{
setTimeout(()=>{
resolve(20)
},0)
})
}
}

等同於

exportdefault{
data(){
return{
count:0
}
},
methods:{
addToCount(n){
this.count+=n
this.$emit('add-to-count',n)
},
resetCount(){
this.count=0
this.$emit('reset')
},
returnValue(){
this.$emit('return-value',10)
},
onInputChange(e){
this.$emit('on-input-change',e.target.value,e)
},
promise(){
constpromise=newPromise(resolve=>{
setTimeout(()=>{
resolve(20)
},0)
})

promise.then(value=>{
this.$emit('promise',value)
})
}
}
}
  • @Watch

@Watch裝飾器是用於屬性監聽,接受一個引數作為監聽屬性,當屬性值發生變化時,會觸發執行被裝飾的函式。

import{Vue,Component,Watch}from'vue-property-decorator'
@Component
exportdefaultclassYourComponentextendsVue{
@Watch('child')
onChildChanged(val:string,oldVal:string){}

@Watch('person',{immediate:true,deep:true})
onPersonChanged1(val:Person,oldVal:Person){}

@Watch('person')
onPersonChanged2(val:Person,oldVal:Person){}
}

等同於

exportdefault{
watch:{
child:[
{
handler:'onChildChanged',
immediate:false,
deep:false
}
],
person:[
{
handler:'onPersonChanged1',
immediate:true,
deep:true
},
{
handler:'onPersonChanged2',
immediate:false,
deep:false
}
]
},
methods:{
onChildChanged(val,oldVal){},
onPersonChanged1(val,oldVal){},
onPersonChanged2(val,oldVal){}
}
}
  • @Ref

@Ref裝飾器接收一個可選引數,用來指向元素或子元件的引用資訊。如果沒有提供這個引數,會使用裝飾器後面的屬性名充當引數

import{Vue,Component,Ref}from'vue-property-decorator'
importAnotherComponentfrom'@/path/to/another-component.vue'
@Component
exportdefaultclassYourComponentextendsVue{
//獲取整個元件
@Ref()readonlyanotherComponent!:AnotherComponent
//獲取具體某個元素
@Ref('aButton')readonlybutton!:HTMLButtonElement
}

等同於

exportdefault{
computed(){
anotherComponent:{
cache:false,
get(){
returnthis.$refs.anotherComponentasAnotherComponent
}
},
button:{
cache:false,
get(){
returnthis.$refs.aButtonasHTMLButtonElement
}
}
}
}

還有其他裝飾器的具體用法可參見官方倉庫:https://github.com/kaorun343/vue-property-decorator

  • 簡單示例:
//TodoList.vue
<template>
<divclass="todo-list">
<divclass="nav">
<a-inputplaceholder="pleaseinputtodo"v-model="inputValue"/>
<a-buttontype="primary"@click="addTodoItem">新增a-button>
div>
<ulclass="list">
<todo-itemv-for="(item,index)intodoList":key="index":item='item':itemIndex='index'
@on-delete='handleDelete'
>{{item.text}}
todo-item>
ul>
div>
template>
<scriptlang='ts'>import{Vue,Component,Watch}from'vue-property-decorator'importTodoItemfrom'@/components/todoListItem'
@Component({name:'todoList',components:{
TodoItem
}
})exportdefaultclassTodoListextendsVue{//data
publicinputValue:string=''
publictodoList:any=[]//methods
privateaddTodoItem():void{if(!this.inputValue.trim().length){return
}letitem={text:this.inputValue
}this.todoList.push(item)this.inputValue=''
}
privatehandleDelete(index:number){this.todoList.splice(index,1)
}//watch
@Watch('inputValue')
onChangeInputValue(val:string,oldVal:string){}
}script>

//TodoItem.vue
<template>
<divclass="list-item">
<liclass="todo-item">
<pclass="text">{{item.text}}p>
<a-icontype="delete"@click.native="deleteItem"/>
li>
div>
template>
<scriptlang='ts'>import{Component,Vue,Prop,Emit}from'vue-property-decorator';
interfaceItemContent{text:string
}
@Component({name:'TodoListItem'
})exportdefaultclassTodoListItemextendsVue{
@Prop({default:{}})publicitem!:ItemContent
@Prop()publicitemIndex!:number
@Emit('on-delete')
privatedeleteItem(){returnthis.itemIndex
}
}script>

3、使用JSX進行元件開發

Vue除了模板語法外,還支援JSX語法,這裡也嘗試用JSX語法定義類元件。

import{Component,Vue,Prop,Emit}from'vue-property-decorator';
import'./todoListItem.scss'
interfaceItemContent{
text:string
}
@Component
exportdefaultclassTodoItemextendsVue{
@Prop({default:{}})publicitem!:ItemContent
@Prop()publicitemIndex!:number

//jsx
protectedrender(){
return(
<liclass='todo-item'><p>{this.item.text}p><a-icontype="delete"nativeOn-click={this.onDelete}/>li>
)
}
@Emit('on-delete')
privatedeleteItem(){
returnthis.itemIndex
}
}

4、引入Vuex進行狀態管理

Vuex作為Vue的狀態管理工具,使得公共資料的管理更加便捷,在類元件的開發中,我們可以使用基於vuexvue-class-componentvuex-class庫。

  • 安裝
npminstall--savevuex-class
  • 基本使用vuex-class提供了四個裝飾器,讓我們可以通過類的方式使用vuex,分別為:@State,@Getter,@Mutation,@Action
importVuefrom'vue'
importComponentfrom'vue-class-component'
import{State,Getter,Action,Mutation}from'vuex-class'
@Component
exportclassmyComponentextendsVue{
//@Statestate中的foo對映到元件的stateFoo
@State('foo')privatestateFoo!:string
//@Gettergetter中的foo對映到元件的getterFoo
@Getter('foo')privategetterFoo!:string
//@Mutation修改state資料的方式,Mutationz中的mutationFoo方法對映到元件的mutationFoo方式
//如果帶引數使用時:this.mutationFoo({key:value}),內部執行了store.commit('mutationFoo', { key: value })
//如果定義了型別,可以使用定義的,這裡any避免ts警告
@Mutation('mutationFoo')privatemutationFoo!:any
//@Action對映action中的方法,action中的actionFoo方法對映到元件的actionFoo
//如果帶引數:this.actionFoo({key:value}),內部執行了store.dispatch('foo', { value: true })
//如果定義了型別,可以使用定義的,這裡any避免ts警告
@Action('actionFoo')privateactionFoo!:any
}

其他更多用法,可以參考官方倉庫:https://github.com/ktsn/vuex-class

說在最後

TS相較於JS真正強大之處在於型別校驗,一定程度上減少了JS飽受詬病的弱型別導致的潛在bug。本文在vue開發之中加入TS,並以類元件的方式進行開發,並不是vue專案開發融入TS所必須的,您即使使用之前的元件開發方式一樣可以使用TS,只是類元件的開發方式能夠以更加扁平化的方式編寫元件。本文中弱化了真正的TS的型別校驗,而這部分才是我們真正值得深入和探索的。