這篇文章主要介紹了怎么寫好一個(gè)vue組件,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
目前成都創(chuàng)新互聯(lián)公司已為上千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間、成都網(wǎng)站托管、企業(yè)網(wǎng)站設(shè)計(jì)、惠安網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。一個(gè)適用性良好的組件,一種是可配置項(xiàng)很多,另一種就是容易覆寫,從而擴(kuò)展功能
Vue 組件的 API 來(lái)自三部分——prop、事件和插槽:
prop 允許外部環(huán)境傳遞數(shù)據(jù)給組件
event 允許從組件內(nèi)觸發(fā)外部環(huán)境的副作用
slot 允許外部環(huán)境將額外的內(nèi)容組合在組件中
prop
組件具有自身狀態(tài),當(dāng)沒有相關(guān) porps 傳入時(shí),使用自身狀態(tài)完成渲染和交互邏輯;當(dāng)該組件被調(diào)用時(shí),如果有相關(guān) props 傳入,那么將會(huì)交出控制權(quán),由父組件控制其行為
僅一個(gè)值傳入組件
如果該組件設(shè)計(jì)上支持雙向綁定,可使用v-model將該參數(shù)傳入組件,減少記憶成本(畢竟 vue 官方的語(yǔ)法糖,不用白不用)
<my-component v-model="foo" />
如果該組件可以獨(dú)立運(yùn)行,不依賴父組件時(shí),還是給這個(gè)值起個(gè)名字吧
<component-no-sync :childNeed="foo" />
很多值需要傳入組件
我們?cè)鹊母附M件寫法:
<child-component :prop1="var1" :prop2="var2" :prop="var3" ... />
其實(shí)可以在父組件上直接使用v-bind={子組件props集合}
傳入一個(gè)對(duì)象
比如當(dāng)一個(gè)組件有諸多配置項(xiàng),且當(dāng)沒有傳入配置項(xiàng)取用組件內(nèi)部默認(rèn)項(xiàng)的時(shí)候,為了方便覆寫子組件的內(nèi)部配置項(xiàng),不妨使用一個(gè)對(duì)象將配置收集到一起,但是這種做法有兩個(gè)缺點(diǎn),謹(jǐn)慎使用
不能利用 props 驗(yàn)證對(duì)象里面每個(gè)的值類型
如果你子組件修改了父組件傳入的對(duì)象A,父組件的對(duì)象A也會(huì)發(fā)生修改,所以我一般只有子組件不會(huì)修改父組件傳入的值的情況下,我才會(huì)傳入對(duì)象。父組件把對(duì)象傳入子組件,是實(shí)現(xiàn)雙向綁定的hack方式,但不推薦,vue 3.0可能就要對(duì)這種方式說(shuō)拜拜了,為了以后代碼好改,還是不要用這種方式實(shí)現(xiàn)雙向綁定
<child-component v-model="text" :setting="{color:'bule'}" /> // 子組件內(nèi)部讀取配置,通過(guò)擴(kuò)展運(yùn)算符替換掉默認(rèn)配置 const setting ={ ...defaultSetting, ...this.setting }
還有一種魚和熊掌兼得的方法,可以給子組件包一層,叫中間組件。父組件將配置項(xiàng)作為一個(gè)對(duì)象傳入中間組件,在中間組件里面對(duì)默認(rèn)配置項(xiàng)進(jìn)行初始化和覆寫,然后再以v-bind={生成好的配置}的方式傳入子組件,在子組件里面進(jìn)行驗(yàn)證。
computed 屬性
vue 的 computed 屬性默認(rèn)是只讀的,你可以提供一個(gè) setter。它可以優(yōu)化我寫組件的邏輯,適用于父組件處理的值和子組件處理的值是同一個(gè)的情況
<template> <el-select v-model="email"> <el-option v-for="item in adminUserOptions" :key="item.email" :label="item.email" :value="item.email" /> </el-select> </template>
export default { props: { value: {} }, computed: { email: { get() { return this.value }, set(val) { this.$emit('input', val) this.$emit('change', val) } } } }
靈活的 prop
我們常看到一些優(yōu)秀的組件庫(kù),傳入的值既可以是一個(gè) String/Number,也可以是一個(gè)函數(shù)。
比如ElementUI的Table組件,當(dāng)你想要顯示樹形數(shù)據(jù)的時(shí)候,必須傳入row-key??此慕榻B就知道是有多靈活:
row-key的作用:行數(shù)據(jù)的 Key,用來(lái)優(yōu)化 Table 的渲染;在使用 reserve-selection 功能與顯示樹形數(shù)據(jù)時(shí),該屬性是必填的。類型為 String 時(shí),支持多層訪問(wèn):user.info.id,但不支持 user.info[0].id,此種情況請(qǐng)使用 Function
處理 rowKey 生成 RowIdentity 的函數(shù)源碼:
//https://github.com/ElemeFE/element/blob/dev/packages/table/src/util.js export const getRowIdentity = (row, rowKey) => { if (!row) throw new Error('row is required when get row identity') // 行數(shù)據(jù)的key if (typeof rowKey === 'string') { if (rowKey.indexOf('.') < 0) { return row[rowKey] } // 支持多層訪問(wèn):user.info.id let key = rowKey.split('.') let current = row for (let i = 0; i < key.length; i++) { current = current[key[i]] } return current // 通過(guò)函數(shù)自定義 // 我處理過(guò)父和子id可能相同的情況,只好通過(guò)Function自定義 // 不可以通過(guò)時(shí)間或者隨機(jī)字符串生成ID } else if (typeof rowKey === 'function') { return rowKey.call(null, row) } }
由于業(yè)務(wù)場(chǎng)景多變,組件的設(shè)計(jì)者很難考慮完全,不妨設(shè)計(jì)靈活的 prop,由開發(fā)者自行定義
事件
emit/on
讀者肯定知道 emit/on 如何使用,我就簡(jiǎn)單說(shuō)一下 vue 的 v-model和sync的語(yǔ)法糖,我們可以利用這些語(yǔ)法糖,幫助我們寫出簡(jiǎn)潔的代碼(父組件可以少寫監(jiān)聽子組件的事件,比如你不用寫@input)
v-model
看一下下面的代碼示例,就能懂這句話了。v-model 會(huì)忽略所有表單元素的 value、checked、selected 特性的初始值而總是將 Vue 實(shí)例的數(shù)據(jù)作為數(shù)據(jù)來(lái)源。你應(yīng)該通過(guò) JavaScript 在組件的 data 選項(xiàng)中聲明初始值
<input v-model="searchText" /> <input v-bind:value="searchText" v-on:input="searchText = $event.target.value" /> // 當(dāng)把v-model用在組件上 <custom-input v-bind:value="searchText" v-on:input="searchText = $event" ></custom-input>
為了讓它正常工作,這個(gè)組件內(nèi)的 <input> 必須:將其 value 特性綁定到一個(gè)名叫 value 的 prop 上在其 input 事件被觸發(fā)時(shí),將新的值通過(guò)自定義的 input 事件拋出,即this.$emit('input',changedValue)
自定義 v-model
為啥要自定義組件的 v-model 呢,因?yàn)閿?shù)據(jù)不符合要求唄。你的輸入值不可能總是 value ,你的事件不可能總是 input,具體詳見文檔
sync(雙向綁定語(yǔ)法糖)
vue 真的是方便了開發(fā)者很多,站在開發(fā)者的角度考慮,很大的提升開發(fā)效率
以 update:myPropName 的模式觸發(fā)事件取代雙向綁定this.$emit('update:title', newTitle),具體詳見文檔
Function 通過(guò) prop 傳入
本來(lái)想放在 prop 部分的,但是個(gè)人覺得其實(shí)它和 emit/on 更有關(guān)系一點(diǎn)
有讀者可能會(huì)問(wèn),為什么不能把子組件里面的事件 emit 出來(lái),通過(guò)父組件處理?然后傳入一個(gè)控制子組件的 prop 屬性。
我想說(shuō)的是,可以,但是這樣真的很麻煩,子組件內(nèi)部的狀態(tài)卻要依賴父組件傳值。
該組件內(nèi)部的狀態(tài),我們需要把它暴露出來(lái)嘛?我覺得不需要,組件內(nèi)部的狀態(tài)就讓它處于組件內(nèi)部
但是可以通過(guò)傳入 function(你可以理解為一個(gè)鉤子),參與組件狀態(tài)變更的行為。比如很好用的拖拽庫(kù),Vue.Draggable控制元素是否被拖動(dòng)的行為。
Vue.Draggable可以傳入一個(gè) move 方法,我們看一下它如何處理的。
onDragMove(evt, originalEvent) { const onMove = this.move; // 如果沒有傳入move,那么返回true,可以移動(dòng) if (!onMove || !this.realList) { return true; } const relatedContext = this.getRelatedContextFromMoveEvent(evt); const draggedContext = this.context; const futureIndex = this.computeFutureIndex(relatedContext, evt); Object.assign(draggedContext, { futureIndex }); const sendEvt = Object.assign({}, evt, { relatedContext, draggedContext }); // 組件行為由傳入的move函數(shù)控制 return onMove(sendEvt, originalEvent); }
這樣做的好處,就是組件內(nèi)部自由一套運(yùn)行邏輯,但是我可以通過(guò)傳入 function 來(lái)干預(yù)。我沒有直接修改組件內(nèi)部狀態(tài),而是通過(guò)函數(shù)(你可以稱它為鉤子)去觸發(fā),方便調(diào)試組件,使得組件行為具有可預(yù)測(cè)性
父組件直接操作子組件
很少有這樣的騷操作,但是由于數(shù)據(jù)和操作的復(fù)雜性,當(dāng)數(shù)據(jù)結(jié)構(gòu)復(fù)雜,嵌套過(guò)深的情況下,父組件很難對(duì)于子組件的數(shù)據(jù)的精細(xì)控制
因此,如果不得已而為之,請(qǐng)?jiān)谖臋n里,把子組件可以調(diào)用的方法暴露出來(lái),供使用者使用。使用這種組件比較麻煩,得去看文檔,沒有文檔的只好去看源碼
ElementUI的tree組件提供了很多方法,用于父組件去操作子組件。
eg:this.$refs.tree.setCheckedKeys([]);
插槽
HTML <slot> element 是 Web Components 技術(shù)的一部分,是自定義 web 組件的占位符,vue 里面的 slot 的靈感來(lái)自 Web Components 規(guī)范草案,具體見文檔
默認(rèn)插槽
能用默認(rèn)插槽就不要使用具名插槽,我真的不想使用你這個(gè)組件的時(shí)候還去翻看你的插槽叫什么名字
之前我司一個(gè)網(wǎng)頁(yè)模板 三個(gè)插槽,header,body,footer,我用的是真的難受,每次都記不得,看似三個(gè)單詞都挺熟悉的,但是其實(shí) head,content,foot 這些單詞也都行啊,誰(shuí)知道用啥(可能我老了吧,組件如果不是必要盡量不要讓人有記憶成本)。
后備內(nèi)容
就是給組件里面的插槽定義默認(rèn)值,它只會(huì)在沒有提供內(nèi)容的時(shí)候被渲染。建議用上插槽就給它添加默認(rèn)內(nèi)容
封裝他人組件
有些時(shí)候我們可能是對(duì)他人的組件進(jìn)行封裝,這里強(qiáng)烈推薦使用v-bind="$attrs" 和 v-on="$listeners"。 vm.$attrs 是一個(gè)屬性,其包含了父作用域中不作為 prop 被識(shí)別 (且獲取) 的特性綁定 (class 和 style 除外)。這些未識(shí)別的屬性可以通過(guò) v-bind="$attrs" 傳入內(nèi)部組件。未識(shí)別的事件可通過(guò)v-on="$listeners"傳入
舉個(gè)例子,比如我創(chuàng)建了我的按鈕組件myButton,封裝了 element-ui 的 el-button 組件(其實(shí)什么事情都沒做),在使用組件 <my-button />時(shí),就可以直接在組件上使用 el-button 的屬性,不被 prop 識(shí)別的屬性會(huì)傳入到 el-button 元素上去
<template> <div> <el-button v-bind="$attrs">導(dǎo)出</el-button> <div> </template> // 父組件使用 <my-button type='primary' size='mini'/>
組件命名
這里推薦遵循 vue 官方指南,值得一看
我們構(gòu)建組件的時(shí)候通常會(huì)將其入口命名為 index.vue ,引入的時(shí)候,直接引入該組件的文件夾即可。
但是這樣做會(huì)有一個(gè)問(wèn)題,當(dāng)你編輯多個(gè)組件的時(shí)候,所有的組件入口都叫做index.vue,容易糊涂
vscode 顯然意識(shí)到了這個(gè)問(wèn)題,所以當(dāng)文件名相同的文件被打開時(shí),它會(huì)在文件名旁邊顯示文件夾名
如何解決呢,我們可以把 index.js 當(dāng)作一個(gè)單純的入口,不承擔(dān)任何邏輯。僅僅負(fù)責(zé)引入component-name-container以及export default component-name-container
my-app └── src └── components └── component-name ├── component-name.css ├── component-name-container.vue └── index.js
tips(個(gè)人喜好)
template,把一個(gè)<template> 元素當(dāng)做不可見的包裹元素,并在上面使用 v-if。最終的渲染結(jié)果將不包含 <template> 元素
能用 computed 計(jì)算屬性的,盡量就不用 watch
模板里面寫太多 v-if 會(huì)讓你的模板很難看,v-else-if盡量還是別用了吧。一長(zhǎng)串的 if else,在模板里面看的很亂
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“怎么寫好一個(gè)vue組件”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!
當(dāng)前標(biāo)題:怎么寫好一個(gè)vue組件-創(chuàng)新互聯(lián)
網(wǎng)站網(wǎng)址:http://muchs.cn/article36/dddhsg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、用戶體驗(yàn)、網(wǎng)站改版、域名注冊(cè)、標(biāo)簽優(yōu)化、網(wǎng)站內(nèi)鏈
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容