小編給大家分享一下如何實(shí)現(xiàn)微信小程序組件化,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
成都創(chuàng)新互聯(lián)公司是一家業(yè)務(wù)范圍包括IDC托管業(yè)務(wù),虛擬空間、主機(jī)租用、主機(jī)托管,四川、重慶、廣東電信服務(wù)器租用,資陽(yáng)移動(dòng)機(jī)房,成都網(wǎng)通服務(wù)器托管,成都服務(wù)器租用,業(yè)務(wù)范圍遍及中國(guó)大陸、港澳臺(tái)以及歐美等多個(gè)國(guó)家及地區(qū)的互聯(lián)網(wǎng)數(shù)據(jù)服務(wù)公司。
本文主要和大家分享微信小程序組件化的解決思路和方法,從小程序基礎(chǔ)庫(kù)版本 1.6.3 開(kāi)始,小程序支持簡(jiǎn)潔的組件化編程。查看自己使用的小程序基礎(chǔ)庫(kù)版本,可以通過(guò)在開(kāi)發(fā)者工具右側(cè)點(diǎn)擊詳情查看:最基本的組件。
小程序的組件,其實(shí)就是一個(gè)目錄,該目錄需要包含4個(gè)文件:
xxx.json
xxx.wxml
xxx.wxss
xxx.js
聲明一個(gè)組件
首先需要在 json 文件中進(jìn)行自定義組件聲明(將 component 字段設(shè)為 true 可這一組文件設(shè)為自定義組件)
{ "component": true }
其次,在要引入組件的頁(yè)面的json文件內(nèi),進(jìn)行引用聲明
{ "usingComponents": { //自定義的組件名稱 : 組件路徑,注意是相對(duì)路徑,不能是絕對(duì)路徑 "component-tag-name": "path/to/the/custom/component" } }
這樣,在主頁(yè)面就可以使用了。
相比于vue的組件引入,小程序的方案更簡(jiǎn)潔。vue組件引入是需要 import 之后,同時(shí)在 components 里面注冊(cè),而小程序的組件只需要在 .json 里面注冊(cè),就可以在 wxml 里面使用。
使用slot
和vue 相同,小程序也有slot概念。
單一slot
在組件模板中可以提供一個(gè)
// 主頁(yè)面內(nèi),是組件 // addlike 組件 我是被slot插入的文本 // 渲染后 hello, 這里是組件 hello, {{my_properties}} hello, 這里是組件 hello, {{my_properties}} 我是被slot插入的文本
多個(gè)slot
如果需要在組件內(nèi)使用多個(gè)slot, 需要在組件js中聲明啟用:
Component({ options: { multipleSlots: true // 在組件定義時(shí)的選項(xiàng)中啟用多slot支持 }, properties: { /* ... */ }, methods: { /* ... */ } })
使用:
// 主頁(yè)面// 在普通的元素上加入 slot 屬性,指定slotname, 就可以變成子元素的slot了 // 子頁(yè)面我是被slot1插入的文本 我是被slot2插入的文本 hello, 這里是組件 hello, {{my_properties}}
Component構(gòu)造器
剛才我們說(shuō)了,一個(gè)組件內(nèi)應(yīng)該包括js, wxml, wxss, json 四個(gè)文件。wxml 相當(dāng)于是 HTML,wxss 相當(dāng)于是 css, 那么js 里面應(yīng)該寫什么呢?
微信官方提供的案例中:
Component({ behaviors: [], properties: { }, data: {}, // 私有數(shù)據(jù),可用于模版渲染 // 生命周期函數(shù),可以為函數(shù),或一個(gè)在methods段中定義的方法名 attached: function(){}, moved: function(){}, detached: function(){}, methods: { onMyButtonTap: function(){ }, _myPrivateMethod: function(){ }, _propertyChange: function(newVal, oldVal) { } } })
里面調(diào)用了一個(gè)Component構(gòu)造器。Component構(gòu)造器可用于定義組件,調(diào)用Component構(gòu)造器時(shí)可以指定組件的屬性、數(shù)據(jù)、方法等。具體 Component里面可以放什么東西,如下所示:
組件與數(shù)據(jù)通信
組件化必然要涉及到數(shù)據(jù)的通信,為了解決數(shù)據(jù)在組件間的維護(hù)問(wèn)題,vue, react,angular 有不同的解決方案。而小程序的解決方案則簡(jiǎn)潔很多。
主頁(yè)面?zhèn)魅霐?shù)據(jù)到組件
properties相當(dāng)于vue的props,是傳入外部數(shù)據(jù)的入口。
// 主頁(yè)面使用組件 // 組件a.js 內(nèi) Component({ properties:{ add_like:{ type:Array, value:[], observer:function(){ } } } })
注意: 傳入的數(shù)據(jù),不管是簡(jiǎn)單數(shù)據(jù)類型,還是引用類型,都如同值復(fù)制一樣(和紅寶書里面描述js函數(shù)參數(shù)傳入是值復(fù)制還不一樣,紅寶書里面的意思是:簡(jiǎn)單數(shù)據(jù)類型直接復(fù)制數(shù)值,引用類型復(fù)制引用,也就是說(shuō)在函數(shù)內(nèi)修改參數(shù)對(duì)象的屬性,會(huì)影響到函數(shù)外對(duì)象的屬性)。
如果是Vue的props, 則可以通過(guò) .sync 來(lái)同步,而在小程序子組件里面,調(diào)用this.setData()修改父組件內(nèi)的數(shù)據(jù),不會(huì)影響到父組件里面的數(shù)據(jù), 也就是說(shuō),子組件property的修改,仿佛和父組件沒(méi)有任何關(guān)系。那么,如果是在子組件內(nèi)修改父組件的數(shù)據(jù),甚至是修改兄弟組件內(nèi)的數(shù)據(jù),有沒(méi)有簡(jiǎn)單的方法呢?下面會(huì)有講到
組件傳出數(shù)據(jù)到主頁(yè)面
和vue類似,組件間交互的主要形式是自定義事件。
組件通過(guò) this.triggerEvent() 觸發(fā)自定義事件,主頁(yè)面在組件上 bind:component_method="main_page_mehod" 來(lái)接收自定義事件。
其中,this.triggerEvent() 方法接收自定義事件名稱外,還接收兩個(gè)對(duì)象,eventDetail 和 eventOptions。
// 子組件觸發(fā)自定義事件 ontap () { // 所有要帶到主頁(yè)面的數(shù)據(jù),都裝在eventDetail里面 var eventDetail = { name:'sssssssss', test:[1,2,3] } // 觸發(fā)事件的選項(xiàng) bubbles是否冒泡,composed是否可穿越組件邊界,capturePhase 是否有捕獲階段 var eventOption = { composed: true } this.triggerEvent('click_btn', eventDetail, eventOption) } // 主頁(yè)面里面 main_page_ontap (eventDetail) { console.log(eventDetail) // eventDetail // changedTouches // currentTarget // target // type // …… // detail 哈哈,所有的子組件的數(shù)據(jù),都通過(guò)該參數(shù)的detail屬性暴露出來(lái) }
組件之間數(shù)據(jù)通信
和vue提出的vuex的解決方案不同,小程序的組件間的通訊簡(jiǎn)單小巧。你可以和主頁(yè)面與組件通訊一樣,使用自定義事件來(lái)進(jìn)行通訊,當(dāng)然更簡(jiǎn)單方便的方法,是使用小程序提供的relations.
relations 是Component 構(gòu)造函數(shù)中的一個(gè)屬性,只要兩個(gè)組件的relations 屬性產(chǎn)生關(guān)聯(lián),他們兩個(gè)之間就可以捕獲到對(duì)方,并且可以相互訪問(wèn),修改對(duì)方的屬性,如同修改自己的屬性一樣。
Component({ relations:{ './path_to_b': { // './path_to_b'是對(duì)方組件的相對(duì)路徑 type: 'child', // type可選擇兩組:parent和child、ancestor和descendant linked:function(target){ } // 鉤子函數(shù),在組件linked時(shí)候被調(diào)用 target是組件的實(shí)例, linkChanged: function(target){} unlinked: function(target){} } }, })
比如說(shuō),有兩個(gè)組件如代碼所示:
// 組件a slot 包含了組件b
他們之間的關(guān)系如下圖所示:
兩個(gè)組件捕獲到對(duì)方組件的實(shí)例,是通過(guò) this.getRelationNodes('./path_to_a')方法。既然獲取到了對(duì)方組件的實(shí)例,那么就可以訪問(wèn)到對(duì)方組件上的data, 也可以設(shè)置對(duì)方組件上的data, 但是不能調(diào)用對(duì)方組件上的方法。
// 在a 組件中 Component({ relations:{ './path_to_b': { type: 'child', linked:function(target){ } // target是組件b的實(shí)例, linkChanged: function(target){} unlinked: function(target){} } }, methods:{ test () { var nodes = this.getRelationNodes('./path_to_b') var component_b = nodes[0]; // 獲取到b組件的數(shù)據(jù) console.log(component_b.data.name) // 設(shè)置父組件的數(shù)據(jù) // 這樣的設(shè)置是無(wú)效的 this.setData({ component_b.data.name:'ss' }) // 需要調(diào)用對(duì)方組件的setData()方法來(lái)設(shè)置 component_b.setData({ name:'ss' }) } } }) // 在b 組件里面 Component({ relations:{ './path_to_a': { //注意!必須雙方組件都聲明relations屬性 type:'parent' } }, data: { name: 'dudu' } }) 注意:1. 主頁(yè)面使用組件的時(shí)候,不能有數(shù)字,比如說(shuō)或 ,可以在主頁(yè)面的json 里面設(shè)置一個(gè)新名字 { "usingComponents":{ "test_component_subb": "../../../components/test_component_sub2/test_component_sub2" } }
2. relations 里面的路徑,比如說(shuō)這里:
是對(duì)方組件真實(shí)的相對(duì)路徑,而不是組件間的邏輯路徑。
3. 如果relations 沒(méi)有關(guān)聯(lián),那么 this.getRelationNodes 是獲取不到對(duì)方組件的
4. 本組件無(wú)法獲取本組件的實(shí)例,使用this.getRelatonsNodes('./ path_to_self ') 會(huì)返回一個(gè)null
4. type 可以選擇的 parent 、 child 、 ancestor 、 descendant
現(xiàn)在我們已經(jīng)可以做到了兩個(gè)組件之間的數(shù)據(jù)傳遞,那么如何在多個(gè)組件間傳遞數(shù)據(jù)呢?
如上圖所示,同級(jí)的組件b 和同級(jí)的組件c , b 和 c 之間不可以直接獲取,b可以獲取到a, c 也可以獲取到a,而a可以直接獲取到 b 和 c。所以,如果想獲取到兄弟元素,需要先獲取到祖先節(jié)點(diǎn),然后再通過(guò)祖先節(jié)點(diǎn)獲取兄弟節(jié)點(diǎn)
我在組件b 里面,我需要先找到祖先組件a的實(shí)例,然后用祖先組件a的實(shí)例的getRelationNodes方法獲取到組件c的實(shí)例。
看見(jiàn)沒(méi)?恐怕我們又要寫一大堆重復(fù)性的代碼了。
幸好,微信小程序還提供了behavior 屬性, 這個(gè)屬性相當(dāng)于 mixin,很容易理解的,是提高代碼復(fù)用性的一種方法。
思路:
假設(shè)目前有三個(gè)組件,組件a, 組件b, 組件c, 其中組件b和組件c是兄弟組件,組建a是b和c的兄弟組件。為了減少代碼的重復(fù)性,我們把獲取父組件的方法,和獲取兄弟組件的方法封裝一下,封裝在 behavior 的 methods 中。只要是引入該behavior的組件,都可以便捷的調(diào)用方法。
實(shí)現(xiàn):
新建一個(gè)behavior文件,命名無(wú)所謂,比如說(shuō)relation_behavior.js
// 在 get_relation.js 文件里面 module.exports = Behavior({ methods:{ // 獲取父組件實(shí)例的快捷方法 _parent () { // 如果根據(jù)該路徑獲取到acestor組件為null,則說(shuō)明this為ancesor var parentNode = this.getRelationNodes('../record_item/record_item') if (parentNode&&parentNode.length !== 0) { return parentNode[0] } else { return this } }, // 獲取兄弟組件實(shí)例的快捷方法 _sibling(name) { var node = this._parent().getRelationNodes(`../${name}/${name}`) if (node &&node.length > 0) { return node[0] } } } })
然后在組件b, 和 組件c 上引入該behavior,并且調(diào)用方法,獲取父組件和兄弟組件的實(shí)例
// 組件b中 var relation_behavior = require('./path_to_relation_behavior') Component({ behaviors:[relation_behavior], methods:{ test () { // 獲得父組件的實(shí)例 let parent = this._parent() // 訪問(wèn)父組件的數(shù)據(jù)d console.log(parent.data.name) // 修改父組件的數(shù)據(jù) parent.setData({ name: 'test1' }) // 獲得兄弟組件的實(shí)例 let sibling = this._sibling('c') // 訪問(wèn)兄弟組件的數(shù)據(jù) console.log(sibling.data.name) // 修改兄弟組件的數(shù)據(jù) sibling.setData({ name:"test" }) } } }) // 組件c中 var relation_behavior = require('./path_to_relation_behavior') Component({ behaviors:[relation_behavior], methods:{ test () { // 獲得父組件的實(shí)例 let parent = this._parent() // 訪問(wèn)父組件的數(shù)據(jù)d console.log(parent.data.name) // 修改父組件的數(shù)據(jù) parent.setData({ name: 'test1' }) // 獲得兄弟組件的實(shí)例 let sibling = this._sibling('b') // 訪問(wèn)兄弟組件的數(shù)據(jù) console.log(sibling.data.name) // 修改兄弟組件的數(shù)據(jù) sibling.setData({ name:"test" }) } } })
同時(shí)需要注意,c和b兩個(gè)組件,從relations屬性的角度來(lái)說(shuō),是a的后代組件。
但是組件b和組件c 所處的作用域, 都是主頁(yè)面的作用域,傳入的property都是主頁(yè)面的property,這樣就保證了組件數(shù)據(jù)的靈活性。relations 像一個(gè)隱形的鏈子一樣把一堆組件關(guān)聯(lián)起來(lái),關(guān)聯(lián)起來(lái)的組件可以相互訪問(wèn),修改對(duì)方的數(shù)據(jù),但是每一個(gè)組件都可以從外界獨(dú)立的獲取數(shù)據(jù)。
看了這么多理論的東西,還是需要一個(gè)具體的場(chǎng)景來(lái)應(yīng)用。
比如說(shuō),我們有個(gè)一個(gè)分享記錄圖片心情的頁(yè)面,當(dāng)用戶點(diǎn)擊【點(diǎn)贊】的按鈕時(shí)候,該心情的記錄 點(diǎn)贊按鈕會(huì)變紅,下面的一欄位置會(huì)多出點(diǎn)贊人的名字。
如果不通過(guò)組件化,很可能的做法是 修改一個(gè)點(diǎn)贊按鈕,然后遍歷數(shù)據(jù)更新數(shù)據(jù),最后所有記錄列表的狀態(tài)都會(huì)被重新渲染一遍。
如果是通過(guò)組件化拆分:把點(diǎn)贊的按鈕封裝為 組件b, 下面點(diǎn)贊人的框封裝為組件c, 每一個(gè)心情記錄都是一個(gè)組件a
下面是代碼實(shí)現(xiàn)
// 在主頁(yè)面內(nèi)// 在組件a內(nèi) var behavior_relation = require('../../relation_behavior.js) //這里引入上文說(shuō)的Behavior Component({ behaviors:[behavior_relation], relations:{ '../b/b':{ type: 'descendant' } } }) // 在組件b內(nèi) var behavior_relation = require('../../relation_behavior.js) //這里引入上文說(shuō)的Behavior Component({ behaviors:[behavior_relation] relations:{ '../a/a':{ type: 'ancestor' } }, data: { is_like: false //控制點(diǎn)贊圖標(biāo)的狀態(tài) }, methods:{ // 當(dāng)用戶點(diǎn)贊的時(shí)候 onClick () { // 修改本組件的狀態(tài) this.setData({ is_like: true }) // 修改 c 組件的數(shù)據(jù) this._sibling('c').setData({ likeStr: this._sibling('c').data.likeStr + '我' }) } } }) // 在組件c內(nèi) var behavior_relation = require('../../relation_behavior.js) //這里引入上文說(shuō)的Behavior Component({ behaviors:[behavior_relation], relations:{ '../a/a':{ type: 'ancestor' } }, data:{ likeStr:'曉紅,小明' } })
這樣,組件b 可以修改組件c中的數(shù)據(jù)。同時(shí),組件b 和 組件c 又可以通過(guò) properties 和 事件系統(tǒng),和主頁(yè)面保持獨(dú)立的數(shù)據(jù)通信。
以上是“如何實(shí)現(xiàn)微信小程序組件化”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!