微信小程序组件化的解决方案
从小程序基础库版本1.6.3 开始,小程序支持简洁的组件化编程。查看自己使用的小程序基础库版本,可以通过在开发者工具右侧点击详情查看 最基本的组件小程序的组件,其实就是一个目录,该目录需要包含4个文件:
声明一个组件首先需要在? { "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>
<{{my_properties}}</slot></slot>
</view>
// 渲染后
<view>
复制代码
多个slot如果需要在组件内使用多个slot, 需要在组件js中声明启用: Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},properties: { /* ... */ },51); font-weight: 700;">methods: { /* ... */ }
})
复制代码
使用: // 主页面
<"sssss">
// 在普通的元素上加入 slot 属性,指定slotname,就可以变成子元素的slot了
<text slot="slot1">我是被slot1插入的文本</text>
<"slot2">我是被slot2插入的文本</addlike>
// 子页面
<"container">
<view>
<slot name="slot1"></slot>
<"slot2"></view>
复制代码
Component构造器刚才我们说了,一个组件内应该包括js,wxml,wxss,json 四个文件。wxml 相当于是 HTML,wxss 相当于是 css,那么js 里面应该写什么呢? 微信官方提供的案例中: Component({
behaviors: [],properties: {
},data: {},// 私有数据,可用于模版渲染
// 生命周期函数,可以为函数,或一个在methods段中定义的方法名
attached: function(){},moved: function(){
},_myPrivateMethod: 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, 则可以通过? 组件传出数据到主页面和vue类似,组件间交互的主要形式是自定义事件。 组件通过? 其中,? ,test:[1,2,0);">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
<a>
<b></b>
</a>
复制代码
他们之间的关系如下图所示: 两个组件捕获到对方组件的实例,是通过 this.getRelationNodes('./path_to_a')方法。既然获取到了对方组件的实例,那么就可以访问到对方组件上的data,也可以设置对方组件上的data,但是不能调用对方组件上的方法。 : {
type: 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. 主页面使用组件的时候,不能有数字,比如说 <component_sub1> 或 <component_sub_1>,可以在主页面的json 里面设置一个新名字 {
"usingComponents":{
"test_component_subb": "../../../components/test_component_sub2/test_component_sub2"
}
}
复制代码
是对方组件真实的相对路径,而不是组件间的逻辑路径。
现在我们已经可以做到了两个组件之间的数据传递,那么如何在多个组件间传递数据呢? 如上图所示,同级的组件b 和同级的组件c,b 和 c 之间不可以直接获取,b可以获取到a, c 也可以获取到a,而a可以直接获取到 b 和 c。所以,如果想获取到兄弟元素,需要先获取到祖先节点,然后再通过祖先节点获取兄弟节点 我在? 看见没?恐怕我们又要写一大堆重复性的代码了。 幸好,微信小程序还提供了behavior 属性,这个属性相当于 mixin,很容易理解的,是提高代码复用性的一种方法。 思路: 假设目前有三个组件,组件a,组件b,组件c, 其中组件b和组件c是兄弟组件,组建a是b和c的兄弟组件。为了减少代码的重复性,我们把获取父组件的方法,和获取兄弟组件的方法封装一下,封装在 behavior 的 methods 中。只要是引入该behavior的组件,都可以便捷的调用方法。 实现: 新建一个behavior文件,命名无所谓,比如说relation_behavior.js )
if (parentNode&&parentNode.length !== 0) {
return parentNode[0]
} else {
return this
}
},136);">// 获取兄弟组件实例的快捷方法
_sibling(name) {
var node = this._parent().getRelationNodes(`../${name}/${name}`)
if (node &&node.length > return node[0]
}
}
}
})
复制代码
然后在组件b,和 组件c 上引入该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中
'b')
"test"
})
}
}
})
复制代码
同时需要注意,c和b两个组件,从relations属性的角度来说,是a的后代组件。 但是组件b和组件c 所处的作用域,都是主页面的作用域,传入的property都是主页面的property,这样就保证了组件数据的灵活性。relations 像一个隐形的链子一样把一堆组件关联起来,关联起来的组件可以相互访问,修改对方的数据,但是每一个组件都可以从外界独立的获取数据。 看了这么多理论的东西,还是需要一个具体的场景来应用。 比如说,我们有个一个分享记录图片心情的页面,当用户点击【点赞】的按钮时候,该心情的记录 点赞按钮会变红,下面的一栏位置会多出点赞人的名字。 如果不通过组件化,很可能的做法是 修改一个点赞按钮,然后遍历数据更新数据,最后所有记录列表的状态都会被重新渲染一遍。 如果是通过组件化拆分:把点赞的按钮封装为? 下面是代码实现 // 在组件a内
var behavior_relation = require('../../relation_behavior.js) //这里引入上文说的Behavior
Component({
behaviors:[behavior_relation],relations:{
'../b/b':{
type: 'descendant'
}
}
})
// 在组件b内
Component({
behaviors:[behavior_relation]
relations:{
'../a/a':{
'ancestor'
}
},data: {
is_like: false //控制点赞图标的状态
},methods:{
// 当用户点赞的时候
onClick () {
// 修改本组件的状态
this.setData({
is_like: true
})
// 修改 c 组件的数据
this._sibling('c').setData({
likeStr: this._sibling('c').data.likeStr + '我'
})
}
}
})
// 在组件c内
(编辑:李大同)
|