孔乙己顯出極高興的樣子,將兩個(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)題
- 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+ ])
- 與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; }
- 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è)廣告~
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é)作之利器。歡迎大家使用~