摘要:觸發(fā)事件可以攜帶數(shù)據(jù),這些數(shù)據(jù)被用于傳遞給綁定了事件的其它組件的回調(diào)函數(shù)上,進(jìn)而被傳遞給其它組件。父組件可以在回調(diào)函數(shù)里做任何事情,頗有靈活性。一般情況下,父組件會在回調(diào)函數(shù)中更新自己的狀態(tài)數(shù)據(jù)。
上一篇博文梳理了vue的數(shù)據(jù)驅(qū)動和響應(yīng)式相關(guān)的特性,這一篇博文就來梳理vue的一個很重要的特性,組件化。
自定義組件之于vue,其意義不亞于函數(shù)之于C,java之類的編程語言。
函數(shù)是計算機(jī)科學(xué)中的一大重要的發(fā)明。
一方面,它代表著一種自頂向下,逐步求精的分而治之的思維,另外一方面,它能夠封裝復(fù)雜實現(xiàn)的細(xì)節(jié),提供更高抽象的接口,降低軟件工程的復(fù)雜度。
在vue中,自定義組件也起著類似的作用。
我們知道,在組件化的GUI界面上,GUI可以被視為一棵樹,瀏覽器的DOM就是一個最好的例子。
從布局上來看,界面可以看成大盒子套小盒子,小盒子再套更小的盒子。。。
反映到DOM上,DOM某節(jié)點的所有子節(jié)點,都是該組件的子組件,都是該組件內(nèi)的元素。
在vue中也是如此,vue組件之間的關(guān)系也是類似DOM一樣,是樹狀的。
在定義一個組件時,需要引用的所有組件,都成為了該組件的子組件。
組件作為一個模塊性質(zhì)的東西,自然就有著它一定的獨立性。而且,與其它模塊的耦合都理所應(yīng)當(dāng)?shù)挠兄鞔_的接口約定。
在vue中,父子組件通信通過組件屬性和事件來進(jìn)行的。
其中,通過組件屬性,父組件的數(shù)據(jù)流向子組件;通過事件,子組件的數(shù)據(jù)流向父組件。
從抽象的角度看,組件作為一個黑盒子,它有著特定的屬性用以接收外部傳遞給它的數(shù)據(jù),它也有著特定的事件,當(dāng)特定操作發(fā)生時調(diào)用回調(diào)函數(shù),以通知別的組件。
組件的屬性 自定義屬性{{ titleMessage }}
上面的例子中,定義了x-child自定義組件,并且在x-parent組件中引用它。
之前在介紹數(shù)據(jù)驅(qū)動的時候,我們知道,定義vue組件時,可以通過data定義組件內(nèi)部的狀態(tài),它是組件數(shù)據(jù)的一部分。
除了data之外,prop(屬性)也是組件數(shù)據(jù)的來源之一,父組件通過prop將自己的數(shù)據(jù)傳遞給子組件。
在定義組件時我們可以看到:
通過props定義組件能夠接收的屬性,甚至還能指定屬性的默認(rèn)值及類型,甚至還能編寫任意的函數(shù)驗證屬性的合法性。明確的指定類似接口聲明,增強(qiáng)可讀性,降低debug難度。
屬性和內(nèi)部狀態(tài)類似,都作為組件數(shù)據(jù)的一部分。區(qū)別在于,在vue設(shè)計上,屬性是只讀的,可以作為數(shù)據(jù)驅(qū)動視圖,但是無法被改變。(我不太清楚vue有沒有從語法強(qiáng)制要求這點,但是良好實踐的vue組件是這樣的)
在使用自定義組件時可以看到:
組件屬性的靈活性特別強(qiáng),你不僅能傳遞給它一個固定的數(shù)據(jù),還能夠使用vue的數(shù)據(jù)綁定語法把父組件內(nèi)的數(shù)據(jù)通過prop傳遞給子組件。
當(dāng)然,這也是響應(yīng)式的。當(dāng)父組件中該數(shù)據(jù)變化時,自然的,傳遞給子組件的數(shù)據(jù)也會變化。那么,子組件中綁定了該數(shù)據(jù)的視圖部分也當(dāng)然會被重新渲染以展現(xiàn)在瀏覽器上。
子組件中定義中的屬性是駝峰寫法,這是符合js編碼規(guī)范的。然而,在引用子組件的地方, 屬性應(yīng)該要寫成短橫線分割式的寫法。
這是因為,html是不區(qū)分大小寫的,vue對此也很無奈。這是在實際編碼中很任意犯的一個錯誤,需要注意。
vue中,屬性被設(shè)計用于父組件傳遞數(shù)據(jù)給子組件的,如果子組件改變了屬性,那么父組件不會受到任何影響。這在vue中被稱為 單向數(shù)據(jù)流。
但是,如果屬性用來傳遞數(shù)組或?qū)ο蟮葟?fù)合的數(shù)據(jù)結(jié)構(gòu),那么可能會出問題??紤]以下的場景:
父組件把數(shù)據(jù)中的對象傳遞給了子組件的屬性。
由于可能修改該屬性,子組件把該屬性直接賦值給內(nèi)部狀態(tài),作為內(nèi)部狀態(tài)修改。
在某些操作觸發(fā)后,該內(nèi)部狀態(tài)被修改。
問題在于,由于js是引用類型語言,簡單的賦值僅僅是傳遞引用,那么,以上場景中,父組件中的數(shù)據(jù),子組件中的屬性,還有子組件中的狀態(tài), 指向的都是同一份對象 !
這會造成一個問題,如果子組件修改了該對象的屬性,那么父組件的數(shù)據(jù)也會受到影響,這破壞了單向數(shù)據(jù)流,會造成很多詭異的bug。
解決方法也很顯然:
方案一是父組件傳遞數(shù)組或?qū)ο蠼o子組件時,使用深拷貝拷貝一份過去,或者子組件將屬性賦值給內(nèi)部狀態(tài)時,深拷貝一份過去,這樣就能夠互不干擾。
方案二是使用不可變數(shù)據(jù)結(jié)構(gòu),每次修改都是產(chǎn)生新的拷貝,因此也能解決問題。
組件的事件 自定義事件以上自定義了x-child組件,并且自定義了組件事件。我們可以看到:
使用vue組件實例的$emit方法,用以觸發(fā)一個自定義事件。觸發(fā)事件可以攜帶數(shù)據(jù),這些數(shù)據(jù)被用于傳遞給綁定了事件的其它組件的回調(diào)函數(shù)上,進(jìn)而被傳遞給其它組件。
不像屬性,自定義事件沒有一個統(tǒng)一聲明的地方,至于為什么我也不清楚。。。得問vue作者去。
該自定義組件內(nèi)部包含了一個按鈕,按鈕被點擊事件觸發(fā)了自定義組件的回調(diào)函數(shù),進(jìn)而觸發(fā)了該自定義組件的自定義事件。
從一個角度來看,該自定義組件像是轉(zhuǎn)發(fā)了原生組件的事件而已。但是從另外一個角度來看,該自定義組件封裝了這些細(xì)節(jié),對外展現(xiàn)的是一個點下按鈕觸發(fā)計數(shù)器增加的事件的這樣一個計數(shù)器。
事件名稱的定義用的短橫線分割的寫法,原因和屬性類似。
{ { counter }}
以上定義了x-parent組件,并且引用了上面定義的子組件??梢钥闯觯?/p>
子組件事件觸發(fā)了父組件的回調(diào)函數(shù),并且將數(shù)據(jù)從回調(diào)函數(shù)中傳入。父組件可以在回調(diào)函數(shù)里做任何事情,頗有靈活性。
一般情況下,父組件會在回調(diào)函數(shù)中更新自己的狀態(tài)數(shù)據(jù)。數(shù)據(jù)更新后觸發(fā)新的視圖渲染,用戶即可在界面上看到了反饋。這樣,通過事件,子組件的數(shù)據(jù)傳遞到了父組件中。
事件綁定的表達(dá)式寫法在監(jiān)聽事件的地方,上面的寫法是使用了一個回調(diào)函數(shù),不過,也可以使用js表達(dá)式,比如:
上面代碼的重點在于arguments[0],如果是js表達(dá)式寫法,使用arguments引用事件的參數(shù),就好像這段js表達(dá)式被放入了一個vue提供的匿名函數(shù),然后使用匿名函數(shù)監(jiān)聽這個事件一樣。
那它有什么用呢?在上面的場景里這樣寫當(dāng)然是不好的,因為削弱了可讀性。
之前在我同事碰到的一個場景里,是一個涉及到插槽分發(fā)作用域的場景,如果寫成回調(diào)函數(shù)的形式,那么在回調(diào)函數(shù)中無法訪問插槽作用域的變量。
因此,必須使用js表達(dá)式的寫法,將插槽作用域中的變量顯式的帶到回調(diào)函數(shù)中,代碼類似這種,懶得構(gòu)造具體的例子了 :
雙向綁定
由于vue設(shè)計的父子組件通信是單向數(shù)據(jù)流,但是由于一些需求的需要,如果能提供雙向數(shù)據(jù)流,會使使用起來更方便。
便捷性和設(shè)計的統(tǒng)一性沖突,怎么辦?當(dāng)然是用語法糖解決了。
實際上,vue提供的兩種好像是雙向數(shù)據(jù)流的機(jī)制,.sync 和 v-model ,都是語法糖。
.sync修飾符這種寫法只是下面的語法糖:
bar = val">
子組件內(nèi),如果修改了foo時,需要觸發(fā)update:foo事件。
v-modelv-model常用于類似表單這樣的自定義控件:
它也是如下語法的語法糖:
插槽foo = val" >
仔細(xì)思考剛才的自定義組件的定義,不難發(fā)現(xiàn),上面的自定義組件只能對DOM中的一棵子樹做抽象和封裝。
那么,考慮這樣一種情況,我們封裝了一個card組件,card的內(nèi)容可以使用任意的vue組件填充。
這種場景,就需要在自定義組件時,能夠在組件的DOM樹里 挖個洞 ,這個洞能夠讓該組件的調(diào)用者填充。
vue提供的這種類似的機(jī)制,被稱為插槽。
我是子組件的標(biāo)題
這是一些初始內(nèi)容
這是更多的初始內(nèi)容
標(biāo)題
這是一些初始內(nèi)容
這是更多的初始內(nèi)容
從上面的示例中可以看到:
在自定義組件時,使用slot標(biāo)簽給自定義組件留了一個“洞”。
在引用該自定義組件時,自定義組件標(biāo)簽內(nèi)部的子元素會填補(bǔ)上這個洞,被渲染出來。
默認(rèn)的插槽只能有一個。可以使用slot標(biāo)簽的name屬性定義插槽名稱以區(qū)分不同的插槽,這樣能夠在自定義組件上挖多個”洞”。
數(shù)據(jù)傳遞vue提供的插槽機(jī)制,在給自定義組件挖”洞”的同時,還能使自定義組件給洞里填充的組件傳遞數(shù)據(jù)。如下:
{{ scope.text }}
從上面可以看出:
在定義slot時,可以通過屬性將數(shù)據(jù)傳遞給它。在引用自定義組件的地方,將插槽內(nèi)容放入template標(biāo)簽內(nèi),通過slot-scope指定變量名,即可在template標(biāo)簽內(nèi)引用該變量從而使用插槽傳遞過來的數(shù)據(jù)。
在實際使用中,一個典型的例子是,表格組件提供插槽自定義表格行的樣式和布局,同時通過插槽將該表格行的數(shù)據(jù)傳遞給插槽內(nèi)容。
最后本篇博文梳理了vue的自定義組件機(jī)制,通過自定義組件,就能夠在vue項目中很好的將項目組件化。
一方面,能夠提取共同的組件進(jìn)行復(fù)用,降低代碼冗余;另外一方面,也能夠提供一種強(qiáng)大的抽象機(jī)制,提高vue的表達(dá)能力。
注:該文于2018-04-10撰寫于我的github靜態(tài)頁博客,現(xiàn)同步到我的segmentfault來。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/95890.html
摘要:前言月份開始出沒社區(qū),現(xiàn)在差不多月了,按照工作的說法,就是差不多過了三個月的試用期,準(zhǔn)備轉(zhuǎn)正了一般來說,差不多到了轉(zhuǎn)正的時候,會進(jìn)行總結(jié)或者分享會議那么今天我就把看過的一些學(xué)習(xí)資源主要是博客,博文推薦分享給大家。 1.前言 6月份開始出沒社區(qū),現(xiàn)在差不多9月了,按照工作的說法,就是差不多過了三個月的試用期,準(zhǔn)備轉(zhuǎn)正了!一般來說,差不多到了轉(zhuǎn)正的時候,會進(jìn)行總結(jié)或者分享會議!那么今天我就...
摘要:由于是需要兼容的后臺系統(tǒng),該項目并不能使用到等技術(shù),因此我在上的經(jīng)驗大都是使用原生的編寫的,可以看見一個組件分為兩部分視圖部分,和數(shù)據(jù)部分。 在公司里幫項目組里開發(fā)后臺系統(tǒng)的前端項目也有一段時間了。 vue這種數(shù)據(jù)驅(qū)動,組件化的框架和react很像,從一開始的快速上手基本的開發(fā),到后來開始自定義組件,對element UI的組件二次封裝以滿足項目需求,期間也是踩了不少坑。由于將來很長一...
摘要:通過裝作這些變化,我們實現(xiàn)了從而到達(dá)了數(shù)據(jù)變化觸發(fā)函數(shù)的過程。于此同時,我們還實現(xiàn)了來擴(kuò)展這個可響應(yīng)的結(jié)構(gòu),讓這個對象擁有了觸發(fā)和響應(yīng)事件的能力。最后,根據(jù)我們的實現(xiàn),這是最終的產(chǎn)出,一個框架,了解一下系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒有看過之前的文章,移步拉到文章末尾查看之前的文章。 provide / inject 在上一步我們實現(xiàn)了,父子組件,和 props 一樣 pr...
摘要:前端每周清單年度總結(jié)與盤點在過去的八個月中,我?guī)缀踔蛔隽藘杉?,工作與整理前端每周清單。本文末尾我會附上清單線索來源與目前共期清單的地址,感謝每一位閱讀鼓勵過的朋友,希望你們能夠繼續(xù)支持未來的每周清單。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清單年度總結(jié)與盤點 在過去的八個月中,我?guī)缀踔蛔隽?..
閱讀 2875·2021-10-08 10:04
閱讀 3355·2021-09-10 11:20
閱讀 576·2019-08-30 10:54
閱讀 3405·2019-08-29 17:25
閱讀 2357·2019-08-29 16:24
閱讀 954·2019-08-29 12:26
閱讀 1524·2019-08-23 18:35
閱讀 2048·2019-08-23 17:53