“茴”字寫法 —— Vue typescript 組件

孔乙己顯出極高興的樣子,將兩個(gè)指頭的長(zhǎng)指甲敲著柜臺(tái)案淋,點(diǎn)頭說(shuō)座韵,“對(duì)呀對(duì)呀!……回字有四樣寫法哎迄,你知道么回右?”我愈不耐煩了,努著嘴走遠(yuǎn)漱挚。孔乙己剛用指甲蘸了酒渺氧,想在柜上寫字旨涝,見(jiàn)
我毫不熱心,便又嘆一口氣侣背,顯出極惋惜的樣子白华。

《“茴”字寫法》系列文章主要總結(jié)常見(jiàn)的代碼操作,給出多種實(shí)現(xiàn)方式贩耐,就像茴香豆的“茴”字有多種寫法一樣弧腥。

規(guī)則千萬(wàn)條,簡(jiǎn)潔第一條
本文鏈接:https://taskhub.work/article/75576861798703104


正文開(kāi)始

Vue 組件有很多中寫法潮太,在3.0之后會(huì)更好的支持typescript管搪,ts用過(guò)都知道虾攻,真香,無(wú)論是代碼提示還是代碼重構(gòu)都非常方便更鲁,本人之前寫過(guò)一個(gè)UI庫(kù)大概4~5萬(wàn)行規(guī)模霎箍,全用ts,期間發(fā)現(xiàn)很多不合理的地方澡为,對(duì)UI庫(kù)進(jìn)行重構(gòu)漂坏,只花了2天時(shí)間。下面通過(guò)對(duì)比不同寫法媒至。

各個(gè)Vue 組件UI庫(kù)實(shí)現(xiàn)方式:

UI 庫(kù) 實(shí)現(xiàn)方式
muse-ui 完全不寫 <template> 只使用 render 函數(shù)
iview 使用 .vue 文件顶别,樣式單獨(dú)寫
element 使用 .vue 文件,樣式單獨(dú)寫
vant 使用 .vue 文件拒啰,樣式單獨(dú)寫
ant-design-vue 使用 .jsx 文件筋夏,樣式單獨(dú)寫
vux 使用帶 <style> 的 .vue 文件,但在使用時(shí)必須用 vux-loader
cube-ui 使用帶 <style> 的 .vue 文件图呢,但有一些配置

在實(shí)際開(kāi)發(fā)中用不用 *.vue 這樣的單文件組件來(lái)開(kāi)發(fā)呢条篷?
網(wǎng)上有很多網(wǎng)友吐槽Vue的單文件組件模式寫法,認(rèn)為Vue的模板語(yǔ)法很雞肋蛤织,各種不方便赴叹,不如全部jsx。決定這個(gè)問(wèn)題的關(guān)鍵是解耦指蚜,包括功能解耦乞巧、模塊解耦、甚至框架解耦摊鸡。*.vue文件組織方式雖然多少和Vue相關(guān)绽媒,但是實(shí)際操作時(shí)發(fā)現(xiàn)要解耦重構(gòu)也不是很大的問(wèn)題,所以還OK免猾。

三種組件寫法對(duì)比

Object API 29 lines

import Vue, { PropOptions } from 'vue'

interface User {
  firstName: string
  lastName: number
}

export default Vue.extend({
  name: 'YourComponent',

  props: {
    user: {
      type: Object,
      required: true
    } as PropOptions<User>
  },

  data () {
    return {
      message: 'This is a message'
    }
  },

  computed: {
    fullName (): string {
      return `${this.user.firstName} ${this.user.lastName}`
    }
  }
})

Class API 17 lines

import { Vue, Component, Prop } from 'vue-property-decorator'

interface User {
  firstName: string
  lastName: number
}

@Component
export default class YourComponent extends Vue {
  @Prop({ type: Object, required: true }) readonly user!: User

  message: string = 'This is a message'

  get fullName (): string {
    return `${this.user.firstName} ${this.user.lastName}`
  }
}

Function API 25 lines

import Vue from 'vue'
import { computed, value } from 'vue-function-api'

interface User {
  firstName: string
  lastName: number
}

interface YourProps {
  user?: User
}

export default Vue.extend({
  name: 'YourComponent',

  setup ({ user }: YourProps) {
    const fullName = computed(() => `${user.firstName} ${user.lastName}`)
    const message = value('This is a message')

    return {
      fullName,
      message
    }
  }
})
寫法 優(yōu)點(diǎn) 缺點(diǎn)
Object API Vue 官方寫法是辕,方便Vue直接處理組件 1. 代碼長(zhǎng)、縮進(jìn)多猎提,組件復(fù)雜時(shí)難以理清邏輯获三,不好進(jìn)行分割
2. 混入較多Vue的概念,新手學(xué)習(xí)成本高
Class API 相關(guān)概念可以用class的思路理解锨苏,可以更好地描述Vue的混入疙教、data、computed伞租,生命周期鉤子等概念贞谓。Vue 3.0 將原生支持class寫法 用到了修飾器語(yǔ)法特性,目前還在實(shí)驗(yàn)階段(typescript可以使用helper函數(shù)解決兼容問(wèn)題葵诈,問(wèn)題不大)
Function API 無(wú)狀態(tài)裸弦,更好的單元測(cè)試祟同、并行化 函數(shù)式寫法很容易寫出回調(diào)地獄,導(dǎo)致代碼可讀性烁兰、可維護(hù)性差耐亏,目前純粹function api 寫法較少見(jiàn)

完成同樣一件事,ts的class寫法簡(jiǎn)潔得多沪斟,在工程較大時(shí)減少1/3左右的代碼广辰,可維護(hù)性大大提高。

typescript class 寫法常見(jiàn)問(wèn)題

  1. route 鉤子無(wú)效問(wèn)題
    使用class寫法會(huì)發(fā)現(xiàn)部分Vue的鉤子函數(shù)無(wú)法使用問(wèn)題主之,可以通過(guò)注冊(cè)鉤子函數(shù)解決择吊,如下:
    import Component from 'vue-class-component'
    
    // Register the router hooks with their names
    Component.registerHooks([
      'beforeRouteEnter',
      'beforeRouteLeave',
      'beforeRouteUpdate' // for vue-router 2.2+
    ])
    
  2. 與Vuex配合使用問(wèn)題
    使用vuex-class 解決,如state映射
    import { Vue, Component } from 'vue-property-decorator';
    import { User } from '@/api/account';
    import { State } from 'vuex-class';
    
    
    @Component
    export default class TestPage extends Vue {
    
      @State(state => state.user, { namespace: 'account' })
      user!: User;
    
    }
    
  3. Vue 混入功能
    代碼經(jīng)常需要各種錯(cuò)誤槽奕,包括用戶輸入錯(cuò)誤几睛、安全檢測(cè)、后臺(tái)錯(cuò)誤粤攒、網(wǎng)絡(luò)故障等所森。如果全部錯(cuò)誤處理代碼放進(jìn)組件中,代碼臃腫夯接,閱讀性差焕济,可以將常見(jiàn)的錯(cuò)誤處理邏輯提取出來(lái),通過(guò)混入的方式插入組件中盔几。class寫法推薦使用vue-property-decorator 的 Mixins晴弃,參考附錄

附錄

附上模板代碼,解決大多數(shù)Vue typescript 組件問(wèn)題
Vue class 組件

import { Vue, Component, Prop, Watch, Model, Mixins } from 'vue-property-decorator';
import { State, Getter, Action, Mutation } from 'vuex-class';
import axios, { AxiosError } from 'axios';

interface Person {
  userId: string;
  nickname: string;
}

@Component
class CommonHandler extends Vue {
  onNetworkError(e: AxiosError) {
    console.log('on error');
  }
}

@Component
export default class Test extends Mixins(CommonHandler) {
  @Prop(Number) readonly propA: number | undefined

  @Prop({ default: 'default value' }) readonly propB!: string

  @Prop([String, Boolean]) readonly propC: string | boolean | undefined

  @Model('change', { type: Boolean }) readonly checked!: boolean
  
  message: string = 'hello world';

  get propBLen(): number {
    return this.propB.length;
  }

  @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) {}
 
  change() {
    console.log('on change');
  }

  created() {
    // 調(diào)用混入中的錯(cuò)誤處理函數(shù)逊拍,簡(jiǎn)化代碼
    axios.get('hello').catch(this.onNetworkError);
  }

  mounted() { console.log('mounted'); }
}

Vue 官方寫法

import axios from 'axios';

const CommonHandler = {
  methods: {
    onNetworkError(e) {
      console.log('on error');
    }
  },
}

export default {
  mixins: [CommonHandler],
  
  props: {
    propA: {
      type: Number
    },
    propB: {
      default: 'default value'
    },
    propC: {
      type: [String, Boolean]
    }
  },

  model: {
    prop: 'checked',
    event: 'change'
  },

  data() {
    return {
      message: 'hello world',
    }
  },

  computed: {
    propBLen() {
      return this.propB.length;
    },
  },

  watch: {
    child: [
      {
        handler: 'onChildChanged',
        immediate: false,
        deep: false
      }
    ],
    person: [
      {
        handler: 'onPersonChanged1',
        immediate: true,
        deep: true
      },
      {
        handler: 'onPersonChanged2',
        immediate: false,
        deep: false
      }
    ]
  },

  methods: {
    change() {
      console.log('on change');
    },

    onChildChanged(val, oldVal) {},

    onPersonChanged1(val, oldVal) {},

    onPersonChanged2(val, oldVal) {}
  },

  // vue lifecycle hooks  
  created() {
    // 調(diào)用混入中的錯(cuò)誤處理函數(shù)上鞠,簡(jiǎn)化代碼
    axios.get('hello').catch(this.onNetworkError);
  },

  mounted() { console.log('mounted'); }
}

52行對(duì)比84行,同樣功能減少38%的代碼

最后打個(gè)廣告~

image

TaskHub 是我們團(tuán)隊(duì)開(kāi)發(fā)的一個(gè) Markdown 加密網(wǎng)盤芯丧,支持常見(jiàn)的任務(wù)管理功能還有Markdown 文件編輯芍阎,所有功能與平臺(tái)解耦,只使用Markdown的特性實(shí)現(xiàn)注整,更好的保障用戶的數(shù)據(jù)安全能曾,實(shí)乃團(tuán)隊(duì)協(xié)作之利器。歡迎大家使用~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肿轨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蕊程,更是在濱河造成了極大的恐慌椒袍,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藻茂,死亡現(xiàn)場(chǎng)離奇詭異驹暑,居然都是意外死亡玫恳,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門优俘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)京办,“玉大人,你說(shuō)我怎么就攤上這事帆焕〔研觯” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵叶雹,是天一觀的道長(zhǎng)财饥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)折晦,這世上最難降的妖魔是什么钥星? 我笑而不...
    開(kāi)封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮满着,結(jié)果婚禮上谦炒,老公的妹妹穿的比我還像新娘。我一直安慰自己风喇,他們只是感情好宁改,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著响驴,像睡著了一般透且。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上豁鲤,一...
    開(kāi)封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天秽誊,我揣著相機(jī)與錄音,去河邊找鬼琳骡。 笑死锅论,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的楣号。 我是一名探鬼主播最易,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼炫狱!你這毒婦竟也來(lái)了藻懒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤视译,失蹤者是張志新(化名)和其女友劉穎嬉荆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體酷含,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鄙早,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年汪茧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片限番。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舱污,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弥虐,到底是詐尸還是另有隱情扩灯,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布躯舔,位于F島的核電站驴剔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏粥庄。R本人自食惡果不足惜丧失,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惜互。 院中可真熱鬧布讹,春花似錦、人聲如沸训堆。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坑鱼。三九已至膘流,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鲁沥,已是汗流浹背呼股。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留画恰,地道東北人彭谁。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像允扇,于是被迫代替她去往敵國(guó)和親缠局。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361