首頁  >  文章  >  微信小程式  >  微信小程式組件化的解決想法與方法

微信小程式組件化的解決想法與方法

小云云
小云云原創
2018-01-25 13:42:513889瀏覽

本文主要和大家分享微信小程式元件化的解決想法與方法,從小程式基礎庫版本 1.6.3 開始,小程式支援簡潔的元件化程式設計。查看自己使用的小程式基礎庫版本,可以透過在開發者工具右側點擊詳情查看:最基本的元件。

小程式的元件,其實就是一個目錄,該目錄需要包含4個檔案:

xxx.json

xxx.wxml

xxx.wxss

xxx.js

##宣告一個元件

首先需要在 json 檔案中進行自訂元件宣告(將 component 欄位設為 true 可這一組檔案設為自訂元件)

{
"component": true
}

其次,在要引入元件的頁面的json檔案內,進行引用宣告

{
"usingComponents": {
//自定义的组件名称     : 组件路径,注意是相对路径,不能是绝对路径  
"component-tag-name": "path/to/the/custom/component"
}
}

這樣,在主頁面就可以使用了。

比起vue的元件引入,小程式的方案更簡潔。 vue元件引進是需要 import 之後,同時在 components 裡面註冊,而小程式的元件只需要在 .json 裡面註冊,就可以在 wxml 裡面使用。

使用slot

和vue 相同,小程式也有slot概念。

單一slot

在元件範本中可以提供一個  節點,用於承載元件引用時提供的子節點。

// 主页面内,<addlike>是组件
<addlike item="item" my_properties="sssss">
<text>我是被slot插入的文本</text>
</addlike>
// addlike 组件
<view class="container">
<view>hello, 这里是组件</view>
<view>hello, {{my_properties}}</view>
<slot></slot>
</view>
// 渲染后
<view class="container">
<view>hello, 这里是组件</view>
<view>hello, {{my_properties}}</view>
<text>我是被slot插入的文本</text>
</view>

多個slot

如果需要在元件內使用多個slot, 需要在元件js中宣告啟用:

Component({
 options: {
   multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
 properties: { /* ... */ },
 methods: { /* ... */ }
})

使用:

// 主页面
<addlike item="item" my_properties="sssss">
// 在普通的元素上加入 slot 属性,指定slotname, 就可以变成子元素的slot了
<text slot="slot1">我是被slot1插入的文本</text>
<text slot="slot2">我是被slot2插入的文本</text>
</addlike>

// 子页面
<view class="container">
<view>hello, 这里是组件</view>
<view>hello, {{my_properties}}</view>
<slot name="slot1"></slot>
<slot name="slot2"></slot>
</view>

Component建構器

剛才我們說了,一個元件內應該包括js, wxml, wxss, json 四個檔案。 wxml 相當於是 HTML,wxss 相當於 css, 那麼js 裡面該寫什麼呢?

微信官方提供的案例中:

Component({
 behaviors: [],
 properties: {
},
 data: {}, // 私有数据,可用于模版渲染
// 生命周期函数,可以为函数,或一个在methods段中定义的方法名
 attached: function(){},
 moved: function(){},
 detached: function(){},
 methods: {
   onMyButtonTap: function(){
},
   _myPrivateMethod: function(){
},
   _propertyChange: function(newVal, oldVal) {
}
}
})

裡面呼叫了一個Component建構器。 Component建構器可用來定義元件,呼叫Component建構器時可以指定元件的屬性、資料、方法等。具體Component裡面可以放什麼東西,如下所示:

微信小程式組件化的解決想法與方法

組件與數據通信

組件化必然要涉及到數據的通信,為了解決數據在組件間的維護問題,vue, react,angular 有不同的解決方案。而小程式的解決方案則簡潔很多。

主頁面傳入資料到元件

properties相當於vue的props,是傳入外部資料的入口。

// 主页面使用组件
<a add_like="{{add_like}}">
</a>
// 组件a.js 内
Component({
   properties:{
       add_like:{
           type:Array,
           value:[],
           observer:function(){
}
}
}
})

注意: 傳入的數據,不管是簡單數據類型,還是引用類型,都如同值複製一樣(和紅寶書裡面描述js函數參數傳入是值複製還不一樣,紅寶書裡面的意思是:簡單資料型別直接複製數值,引用型別複製引用,也就是說在函數內修改參數物件的屬性,會影響到函數外物件的屬性)。

如果是Vue的props, 則可以透過.sync 來同步,而在小程式子元件裡面,呼叫this.setData()修改父元件內的數據,不會影響到父元件裡面的數據, 也就是說,子組件property的修改,彷彿和父組件沒有任何關係。那麼,如果是在子元件內修改父元件的數據,甚至是修改兄弟元件內的數據,有沒有簡單的方法呢?下面會有講到

元件傳出資料到主頁面

和vue類似,元件間互動的主要形式是自訂事件。

元件透過 this.triggerEvent() 觸發自訂事件,主頁面在元件上 bind:component_method="main_page_mehod" 來接收自訂事件。

其中,this.triggerEvent() 方法接收自訂事件名稱外,也接收兩個對象,eventDetail 和 eventOptions。

// 子组件触发自定义事件
ontap () {
// 所有要带到主页面的数据,都装在eventDetail里面
var eventDetail = {
name:&#39;sssssssss&#39;,
test:[1,2,3]
}
// 触发事件的选项 bubbles是否冒泡,composed是否可穿越组件边界,capturePhase 是否有捕获阶段
var eventOption = {
composed: true
}
this.triggerEvent(&#39;click_btn&#39;, eventDetail, eventOption)
}
// 主页面里面
main_page_ontap (eventDetail) {
   console.log(eventDetail)
// eventDetail
// changedTouches
// currentTarget
// target
// type
// ……
// detail   哈哈,所有的子组件的数据,都通过该参数的detail属性暴露出来
}

元件之間資料通訊

和vue提出的vuex的解決方案不同,小程式的元件間的通訊簡單小巧。你可以和主頁面與元件通訊一樣,使用自訂事件來進行通訊,當然更簡單方便的方法,是使用小程式提供的relations.

relations 是Component 建構子中的屬性,只要兩個組件的relations 屬性產生關聯,他們兩個之間就可以捕獲到對方,並且可以相互訪問,修改對方的屬性,如同修改自己的屬性一樣。

Component({
  relations:{
&#39;./path_to_b&#39;: { // &#39;./path_to_b&#39;是对方组件的相对路径
       type: &#39;child&#39;, //  type可选择两组:parent和child、ancestor和descendant
       linked:function(target){ } // 钩子函数,在组件linked时候被调用 target是组件的实例,
       linkChanged: function(target){}
       unlinked: function(target){}
}
},
})

比如說,有兩個元件如程式碼所示:

// 组件a slot 包含了组件b<a><b></b></a>

他們之間的關係如下圖所示:

微信小程式組件化的解決想法與方法

兩個元件捕捉對方元件的實例,是透過this.getRelationNodes('./path_to_a')方法。既然取得到了對方元件的實例,那麼就可以存取到對方元件上的data, 也可以設定對方元件上的data, 但是不能呼叫對方元件上的方法。

// 在a 组件中
Component({
  relations:{
&#39;./path_to_b&#39;: {
          type: &#39;child&#39;,
          linked:function(target){ } // target是组件b的实例,
          linkChanged: function(target){}
          unlinked: function(target){}
}
},
  methods:{
      test () {
var nodes = this.getRelationNodes(&#39;./path_to_b&#39;)
var component_b = nodes[0];

// 获取到b组件的数据
          console.log(component_b.data.name)

// 设置父组件的数据
// 这样的设置是无效的
this.setData({
              component_b.data.name:&#39;ss&#39;
})
// 需要调用对方组件的setData()方法来设置
          component_b.setData({
              name:&#39;ss&#39;
})
}
}
})

// 在b 组件里面
Component({
  relations:{
&#39;./path_to_a&#39;: { //注意!必须双方组件都声明relations属性
          type:&#39;parent&#39;
}
},
  data: {
      name: &#39;dudu&#39;
}
})
注意:1. 主页面使用组件的时候,不能有数字,比如说 <component_sub1> 或 <component_sub_1>,可以在主页面的json 里面设置一个新名字
{
"usingComponents":{
"test_component_subb": "../../../components/test_component_sub2/test_component_sub2"
}
}

2. relations 裡面的路徑,比如說這裡:

微信小程式組件化的解決想法與方法

#是對方元件真實的相對路徑,而不是元件間的邏輯路徑。

3. 如果relations 沒有關聯,那麼 this.getRelationNodes 是取得不到對方元件的

4. 本组件无法获取本组件的实例,使用this.getRelatonsNodes('./ path_to_self ') 会返回一个null

4. type 可以选择的 parent 、 child 、 ancestor 、 descendant 

现在我们已经可以做到了两个组件之间的数据传递,那么如何在多个组件间传递数据呢?

3微信小程式組件化的解決想法與方法

如上图所示,同级的组件b 和同级的组件c , b 和 c 之间不可以直接获取,b可以获取到a, c 也可以获取到a,而a可以直接获取到 b 和 c。所以,如果想获取到兄弟元素,需要先获取到祖先节点,然后再通过祖先节点获取兄弟节点

我在组件b 里面,我需要先找到祖先组件a的实例,然后用祖先组件a的实例的getRelationNodes方法获取到组件c的实例。

看见没?恐怕我们又要写一大堆重复性的代码了。

幸好,微信小程序还提供了behavior 属性, 这个属性相当于 mixin,很容易理解的,是提高代码复用性的一种方法。

思路:

假设目前有三个组件,组件a, 组件b, 组件c, 其中组件b和组件c是兄弟组件,组建a是b和c的兄弟组件。为了减少代码的重复性,我们把获取父组件的方法,和获取兄弟组件的方法封装一下,封装在 behavior 的 methods 中。只要是引入该behavior的组件,都可以便捷的调用方法。

实现:

新建一个behavior文件,命名无所谓,比如说relation_behavior.js

// 在 get_relation.js 文件里面
module.exports = Behavior({
methods:{
// 获取父组件实例的快捷方法
_parent () {
// 如果根据该路径获取到acestor组件为null,则说明this为ancesor
var parentNode = this.getRelationNodes(&#39;../record_item/record_item&#39;)
if (parentNode&&parentNode.length !== 0) {
return parentNode[0]
} else {
return this
}
},
// 获取兄弟组件实例的快捷方法
_sibling(name) {
var node = this._parent().getRelationNodes(`../${name}/${name}`)
if (node &&node.length > 0) {
return node[0]
}
}
}
})

然后在组件b, 和 组件c 上引入该behavior,并且调用方法,获取父组件和兄弟组件的实例

// 组件b中
var relation_behavior = require(&#39;./path_to_relation_behavior&#39;)
Component({
   behaviors:[relation_behavior],
   methods:{
       test () {
// 获得父组件的实例
let parent = this._parent()
// 访问父组件的数据d
           console.log(parent.data.name)
// 修改父组件的数据
           parent.setData({
               name: &#39;test1&#39;
})
// 获得兄弟组件的实例
let sibling = this._sibling(&#39;c&#39;)
// 访问兄弟组件的数据
           console.log(sibling.data.name)
// 修改兄弟组件的数据
           sibling.setData({
               name:"test"
})
}
}
})
// 组件c中
var relation_behavior = require(&#39;./path_to_relation_behavior&#39;)
Component({
   behaviors:[relation_behavior],
   methods:{
       test () {
// 获得父组件的实例
let parent = this._parent()
// 访问父组件的数据d
           console.log(parent.data.name)
// 修改父组件的数据
           parent.setData({
               name: &#39;test1&#39;
})
// 获得兄弟组件的实例
let sibling = this._sibling(&#39;b&#39;)
// 访问兄弟组件的数据
           console.log(sibling.data.name)
// 修改兄弟组件的数据
           sibling.setData({
               name:"test"
})
}
}
})

同时需要注意,c和b两个组件,从relations属性的角度来说,是a的后代组件。

但是组件b和组件c 所处的作用域, 都是主页面的作用域,传入的property都是主页面的property,这样就保证了组件数据的灵活性。relations 像一个隐形的链子一样把一堆组件关联起来,关联起来的组件可以相互访问,修改对方的数据,但是每一个组件都可以从外界独立的获取数据。

看了这么多理论的东西,还是需要一个具体的场景来应用。

比如说,我们有个一个分享记录图片心情的页面,当用户点击【点赞】的按钮时候,该心情的记录 点赞按钮会变红,下面的一栏位置会多出点赞人的名字。

微信小程式組件化的解決想法與方法

如果不通过组件化,很可能的做法是 修改一个点赞按钮,然后遍历数据更新数据,最后所有记录列表的状态都会被重新渲染一遍。

如果是通过组件化拆分:把点赞的按钮封装为 组件b, 下面点赞人的框封装为组件c, 每一个心情记录都是一个组件a

微信小程式組件化的解決想法與方法

下面是代码实现

// 在主页面内
<view wx:for=&#39;{{feed_item}}&#39;>
<a item=&#39;{{item}}&#39;>
<b></b>
<c></c>
</a>
<view>
// 在组件a内
var behavior_relation = require(&#39;../../relation_behavior.js)  //这里引入上文说的Behavior
Component({
   behaviors:[behavior_relation],
   relations:{
       &#39;../b/b&#39;:{
           type: &#39;descendant&#39;
       }
   }
})
// 在组件b内
var behavior_relation = require(&#39;../../relation_behavior.js) //这里引入上文说的Behavior
Component({
   behaviors:[behavior_relation]
   relations:{
&#39;../a/a&#39;:{
           type: &#39;ancestor&#39;
}
},
   data: {
       is_like: false //控制点赞图标的状态
},
   methods:{
// 当用户点赞的时候
       onClick () {
//  修改本组件的状态
this.setData({
               is_like: true
})
// 修改 c 组件的数据
this._sibling(&#39;c&#39;).setData({
               likeStr: this._sibling(&#39;c&#39;).data.likeStr + &#39;我&#39; 
})
}
}
})
// 在组件c内
var behavior_relation = require(&#39;../../relation_behavior.js)  //这里引入上文说的Behavior
Component({
   behaviors:[behavior_relation],
   relations:{
       &#39;../a/a&#39;:{
           type: &#39;ancestor&#39;
       }
   },
   data:{
       likeStr:&#39;晓红,小明&#39;
   }
})

这样,组件b 可以修改组件c中的数据。同时,组件b 和 组件c 又可以通过 properties 和 事件系统,和主页面保持独立的数据通信。

相关推荐:

微信小程序版2048小游戏

微信小程序实现流程进度功能实例分享

微信小程序实现上传头像详解

以上是微信小程式組件化的解決想法與方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn