频道栏目
首页 > 资讯 > 微信小程序 > 正文

微信小程序组件化的解决案例展示

19-02-12        来源:[db:作者]  
收藏   我要投稿

从小程序基础库版本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"

}

}

复制代码

component-tag-name字段是自定义的组件名称

后面的是组件路径,注意是相对路径,不能是绝对路径

这样,在主页面就可以使用了。

相比于vue的组件引入,小程序的方案更简洁。vue组件引入是需要 import 之后,同时在 components 里面注册,而小程序的组件只需要在 .json 里面注册,就可以在 wxml 里面使用。

使用slot

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

单一slot

在组件模板中可以提供一个节点,用于承载组件引用时提供的子节点。

// 主页面内,是组件

我是被slot插入的文本

// addlike 组件

hello, 这里是组件

hello, {{my_properties}}

// 渲染后

hello, 这里是组件

hello, {{my_properties}}

我是被slot插入的文本

复制代码

多个slot

如果需要在组件内使用多个slot, 需要在组件js中声明启用:

Component({

options: {

multipleSlots: true // 在组件定义时的选项中启用多slot支持

},

properties: { /* ... */ },

methods: { /* ... */ }

})

复制代码

使用:

// 主页面

// 在普通的元素上加入 slot 属性,指定slotname, 就可以变成子元素的slot了

我是被slot1插入的文本

我是被slot2插入的文本

// 子页面

hello, 这里是组件

hello, {{my_properties}}

复制代码

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.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:'sssssssss',

test:[1,2,3]

}

// 触发事件的选项 bubbles是否冒泡,composed是否可穿越组件边界,capturePhase 是否有捕获阶段

var eventOption = {

composed: true

}

this.triggerEvent('click_btn', eventDetail, eventOption)

}

// 主页面里面

main_page_ontap (eventDetail) {

console.log(eventDetail)

// eventDetail

// changedTouches

// currentTarget

// target

// type

// ……

// detail 哈哈,所有的子组件的数据,都通过该参数的detail属性暴露出来

}

复制代码

组件之间数据通信

和vue提出的vuex的解决方案不同,小程序的组件间的通讯简单小巧。你可以和主页面与组件通讯一样,使用自定义事件来进行通讯,当然更简单方便的方法,是使用小程序提供的relations.

relations 是Component 构造函数中的一个属性,只要两个组件的relations 属性产生关联,他们两个之间就可以捕获到对方,并且可以相互访问,修改对方的属性,如同修改自己的属性一样。

Component({

relations:{

'./path_to_b': { // './path_to_b'是对方组件的相对路径

type: 'child', // type可选择两组:parent和child、ancestor和descendant

linked:function(target){ } // 钩子函数,在组件linked时候被调用 target是组件的实例,

linkChanged: function(target){}

unlinked: function(target){}

}

},

})

复制代码

比如说,有两个组件如代码所示:

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

复制代码

他们之间的关系如下图所示:

 

 

两个组件捕获到对方组件的实例,是通过 this.getRelationNodes('./path_to_a')方法。既然获取到了对方组件的实例,那么就可以访问到对方组件上的data, 也可以设置对方组件上的data, 但是不能调用对方组件上的方法。

// 在a 组件中

Component({

relations:{

'./path_to_b': {

type: 'child',

linked:function(target){ } // target是组件b的实例,

linkChanged: function(target){}

unlinked: function(target){}

}

},

methods:{

test () {

var nodes = this.getRelationNodes('./path_to_b')

var component_b = nodes[0];

// 获取到b组件的数据

console.log(component_b.data.name)

// 设置父组件的数据

// 这样的设置是无效的

this.setData({

component_b.data.name:'ss'

})

// 需要调用对方组件的setData()方法来设置

component_b.setData({

name:'ss'

})

}

}

})

// 在b 组件里面

Component({

relations:{

'./path_to_a': { //注意!必须双方组件都声明relations属性

type:'parent'

}

},

data: {

name: 'dudu'

}

})

复制代码

注意:1. 主页面使用组件的时候,不能有数字,比如说 或 ,可以在主页面的json 里面设置一个新名字

{

"usingComponents":{

"test_component_subb": "../../../components/test_component_sub2/test_component_sub2"

}

}

复制代码

relations 里面的路径,比如说这里:

 

 

是对方组件真实的相对路径,而不是组件间的逻辑路径。

如果relations 没有关联,那么 this.getRelationNodes 是获取不到对方组件的

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

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

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

 

 

如上图所示,同级的组件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('../record_item/record_item')

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('./path_to_relation_behavior')

Component({

behaviors:[relation_behavior],

methods:{

test () {

// 获得父组件的实例

let parent = this._parent()

// 访问父组件的数据d

console.log(parent.data.name)

// 修改父组件的数据

parent.setData({

name: 'test1'

})

// 获得兄弟组件的实例

let sibling = this._sibling('c')

// 访问兄弟组件的数据

console.log(sibling.data.name)

// 修改兄弟组件的数据

sibling.setData({

name:"test"

})

}

}

})

// 组件c中

var relation_behavior = require('./path_to_relation_behavior')

Component({

behaviors:[relation_behavior],

methods:{

test () {

// 获得父组件的实例

let parent = this._parent()

// 访问父组件的数据d

console.log(parent.data.name)

// 修改父组件的数据

parent.setData({

name: 'test1'

})

// 获得兄弟组件的实例

let sibling = this._sibling('b')

// 访问兄弟组件的数据

console.log(sibling.data.name)

// 修改兄弟组件的数据

sibling.setData({

name:"test"

})

}

}

})

复制代码

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

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

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

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

 

 

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

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

 

 

下面是代码实现

// 在主页面内

// 在组件a内

var behavior_relation = require('../../relation_behavior.js) //这里引入上文说的Behavior

Component({

behaviors:[behavior_relation],

relations:{

'../b/b':{

type: 'descendant'

}

}

})

// 在组件b内

var behavior_relation = require('../../relation_behavior.js) //这里引入上文说的Behavior

Component({

behaviors:[behavior_relation]

relations:{

'../a/a':{

type: 'ancestor'

}

},

data: {

is_like: false //控制点赞图标的状态

},

methods:{

// 当用户点赞的时候

onClick () {

// 修改本组件的状态

this.setData({

is_like: true

})

// 修改 c 组件的数据

this._sibling('c').setData({

likeStr: this._sibling('c').data.likeStr + '我'

})

}

}

})

// 在组件c内

var behavior_relation = require('../../relation_behavior.js) //这里引入上文说的Behavior

Component({

behaviors:[behavior_relation],

relations:{

'../a/a':{

type: 'ancestor'

}

},

data:{

likeStr:'晓红,小明'

}

})

复制代码

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

相关TAG标签
上一篇:微信小程序之富文本图片宽度自适应教程
下一篇:kubernetes之kubectl命令用法总结
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站