1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Vue3 擁抱 TypeScript 的完整項目結構搭建

        共 8486字,需瀏覽 17分鐘

         ·

        2022-03-10 23:41

        大廠技術??高級前端??Node進階

        點擊上方?程序員成長指北,關注公眾號

        回復1,加入高級Node交流群

        一個完整的Vue3+Ts項目,支持.vue和.tsx寫法

        項目地址:https://github.com/vincentzyc/vue3-demo.git

        作者:TinssonTai

        https://juejin.im/post/6875713523968802829

        TypeScript?是JS的一個超集,主要提供了類型系統(tǒng)和對ES6的支持,使用?TypeScript?可以增加代碼的可讀性和可維護性,在?react?和?vue?社區(qū)中也越來越多人開始使用TypeScript。從最近發(fā)布的?Vue3?正式版本來看,?Vue3?的源碼就是用?TypeScript?編寫的,更好的?TypeScript?支持也是這一次升級的亮點。當然,在實際開發(fā)中如何正確擁抱?TypeScript?也是遷移至?Vue3?的一個小痛點,這里就針對?Vue3?和?TypeScript?展開一些交流。

        96.8%的代碼都是TypeScript,支持的力度也是相當大??

        • Vue3入口:?https://github.com/vuejs/vue-next

        項目搭建

        在官方倉庫的 Quickstart 中推薦用兩種方式方式來構建我們的?SPA?項目:

        • vite
        npm?init?vite-app?sail-vue3?#?OR?yarn?create?vite-app?sail-vue3
        • vue-cli
        npm?install?-g?@vue/cli?#?OR?yarn?global?add?@vue/cli
        vue?create?sail-vue3
        #?select?vue?3?preset

        vite?是一個由原生ESM驅動的Web開發(fā)構建工具,打開?vite?依賴的?package.json?可以發(fā)現(xiàn)在?devDependencies?開發(fā)依賴里面已經引入了TypeScript?,甚至還有?vuex?,?vue-router?,?less?,?sass?這些本地開發(fā)經常需要用到的工具。vite?輕量,開箱即用的特點,滿足了大部分開發(fā)場景的需求,作為快速啟動本地?Vue?項目來說,這是一個非常完美的工具。

        后面的演示代碼也是用vite搭的

        從?vue2.x?走過來的掘友肯定知道?vue-cli?這個官方腳手架,?vue3?的更新怎么能少得了?vue-cli?呢,?vue-cli?更強調的是用?cli?的方式進行交互式的配置,選擇起來更加靈活可控。豐富的官方插件適配,GUI的創(chuàng)建管理界面,標準化開發(fā)流程,這些都是?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?不是一件簡單的事情:

        1. 要用?vue-class-component?強化?vue?組件,讓?Script?支持?TypeScript?裝飾器
        2. 用?vue-property-decorator?來增加更多結合?Vue?特性的裝飾器
        3. 引入?ts-loader?讓?webpack?識別?.ts?.tsx?文件
        4. .....

        然后出來的代碼風格是這樣的:

        @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:?string
        ??age:?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?中返回的數(shù)據(jù)也能在不顯式定義類型的時候推斷出大多類型,?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?//?OK
        ??const?result?=?year.value.split('')?//?=>?Property?'split'?does?not?exist?on?type?'number'
        ?}
        })

        分析上面的代碼,可以發(fā)現(xiàn)如果我們不給定?ref?定義的類型的話,?vue3?也能根據(jù)初始值來進行類型推導,然后需要指定復雜類型的時候簡單傳遞一個泛型即可。

        Tips:如果只有setup方法的話,可以直接在defineComponent中傳入setup函數(shù)

        const?Component?=?defineComponent(()?=>?{
        ????const?year?=?ref(2020)
        ????const?month?=?ref('9')

        ????month.value?=?9?//?OK
        ????const?result?=?year.value.split('')?//?=>?Property?'split'?does?not?exist?on?type?'number'
        })

        reactive

        import?{?defineComponent,?reactive?}?from?'vue'

        interface?Student?{
        ??name:?string
        ??class?:?string
        ??age:?number
        }

        export?default?defineComponent(
        {
        ??name:?'HelloWorld',
        ??setup()?{
        ????const?student?=?reactive({?name:?'阿勇',?age:?16?})
        ????//?or
        ????const?student:?Student?=?reactive({?name:?'阿勇',?age:?16?})
        ????//?or
        ????const?student?=?reactive({?name:?'阿勇',?age:?16,?class:?'cs'?})?as?Student
        ??}
        })

        聲明?reactive?的時候就很推薦使用接口了,然后怎么使用類型斷言就有很多種選擇了,這是?TypeScript?的語法糖,本質上都是一樣的。

        自定義Hooks

        vue3?借鑒?react hooks?開發(fā)出了?Composition API?,那么也就意味著?Composition API?也能進行自定義封裝?hooks?,接下來我們就用?TypeScript?風格封裝一個計數(shù)器邏輯的?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)?return
        ????if?(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?函數(shù)的入參類型和返回類型進行了定義,入參的?Range?和返回的?Result?分別用一個接口來指定,這樣做了以后,最大的好處就是在使用?useCount?函數(shù)的時候,ide就會自動提示哪些參數(shù)是必填項,各個參數(shù)的類型是什么,防止業(yè)務邏輯出錯。

        接下來,在增加?inc?和減少?dec?的兩個函數(shù)中增加了?typeo?類型守衛(wèi)檢查,因為傳入的?delta?類型值在某些特定場景下不是很確定,比如在?template?中調用方法的話,類型檢查可能會失效,傳入的類型就是一個原生的?Event?。

        關于?ref?類型值,這里并沒有特別聲明類型,因為?vue3?會進行自動類型推導,但如果是復雜類型的話可以采用類型斷言的方式:ref(initObj) as Ref

        小建議 ??

        AnyScript

        在初期使用?TypeScript?的時候,很多掘友都很喜歡使用?any?類型,硬生生把TypeScript?寫成了?AnyScript?,雖然使用起來很方便,但是這就失去了?TypeScript?的類型檢查意義了,當然寫類型的習慣是需要慢慢去養(yǎng)成的,不用急于一時。

        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寫法







        "{?color?}">
        .text-color?{
        ??color:?var(--color);
        }

        • tsx寫法
        import?{?ref,?reactive?}?from?"vue";
        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:?'浙江省杭州市西湖區(qū)文三路?138?號東方通信大廈?7?樓?501?室',
        ????????isDefault:?true,
        ??????},
        ??????{
        ????????id:?'2',
        ????????name:?'李四',
        ????????tel:?'1310000000',
        ????????address:?'浙江省杭州市拱墅區(qū)莫干山路?50?號',
        ??????},
        ????])
        ????const?disabledList?=?reactive([
        ??????{
        ????????id:?'3',
        ????????name:?'王五',
        ????????tel:?'1320000000',
        ????????address:?'浙江省杭州市濱江區(qū)江南大道?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?(
        ????????
        ??????????????????????title="地址管理"
        ????????????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?的姿勢更加從容優(yōu)雅,?Vue?面向大型項目開發(fā)也更加有底氣了,點擊查看更多。

        Node 社群



        我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。



        如果你覺得這篇內容對你有幫助,我想請你幫我2個小忙:

        1. 點個「在看」,讓更多人也能看到這篇文章
        2. 訂閱官方博客?www.inode.club?讓我們一起成長

        點贊和在看就是最大的支持??

        瀏覽 48
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            偷窥高潮呻吟啪啪另类 | 国产操比 | 坐公交忘穿内裤被挺进老苏晴 | 逼特逼成人| 亂伦AV综合一区 | 免费的羞羞视频 | 成人精品免费 | 插逼免费视频 | 影音先锋中文资源 | 黄色网址在线播放 |