一、自定义组件介绍
微信小程序提供了自定义组件扩展机制,允许我们使用自定义组件的方式来构建页面。
自定义组件可以使我们更好的复用一些功能。
二、创建自定义组件
在项目的pages文件夹
下创建文件夹,例如customComponent文件
夹,然后右键这个文件夹,选择新建Component,这时customComponent文件夹
出现四个文件,和正常的创建的一样,下面介绍这四个文件和正常的不同之处:
#CusComponent.js:
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})
#CusComponent.json
{
"component": true, //代表当前文件是一个组件
"usingComponents": {} //使用组件写在括号里
}
js文件的头有Page
变为Component
,json文件里面多了"component": true,
。wxml
和wxss
文件和原来的一样。
三、如何使用自定义组件
想在主页index
页面使用组件,那么打开存放页面的pages
文件夹下的index
文件夹中的index.json
,在usingComponents
中添加如下代码:
{
"usingComponents": {
"CusComponent":"/pages/customComponent/cusComponent"
}
}
usingComponents
里面是键值对,左边是要使用组件的名字,右边是其路径。
接着在index.wxml
中使用
引入组件。
四、自定义组件中.js
文件的属性介绍
- data属性:组件的初始数据
例如添加如下数据:
#js文件中的data属性:
data: {
itemList:[
{id:0,name:"首页"},
{id:1,name:"类别"},
{id:2,name:"关于"},
]
}
此时在wxml
文件中可以像下面这样写:
{{item.name}}
结果:首页 类别 关于
因为wx:for
中列表遍历默认的是item,所以可以直接用item.name
来获取name
。
- methods属性:组件的方法列表
在正常页面中方法是存放在.js文件
中data属性
的同层级下的。而在自定义组件中必须放在.js文件
的methods
里面,放在外面方法无效。
methods: {
handleMethod(e){
//语句let itemList = this.data.itemList;和下面的相等,解构
let {itemList} = this.data;
//[].forEach遍历数组,遍历的时候修改了v,也会导致源数组被修改
//使用这种写法则相当于重新拷贝了一份数据let items = JSON.parse(JSON.stringify(this.data.itemList));
itemList.forEach((v,i) => i===index?v.isActive=true:v.isActive=false)
}
}
- properties属性:组件的对外属性,是属性名到属性设置的映射表。里面存放的是父组件传递过来的数据
参见:五、父组件向子组件传值 - options属性:配置一些功能的选项
可配置的选项:
multipleSlots:true,在组件定义时的选项中启动多slot支持
styleIsolation:isolated,启动样式隔离,具体配置选项参见:(八)自定义组件的样式
addGlobalClass:true,表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面。这个选项等价于设置 styleIsolation: apply-shared
virtualHost:true,将自定义节点设置成虚拟的,我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等,而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定。
/* 组件 custom-component.js */
Component({
options: {
addGlobalClass: true,
}
})
- 其他属性:
属性 | 类型 | 是否必填 | 描述 |
---|---|---|---|
properties | Object|Map | 否 | 组件的对外属性,是属性名到属性设置的映射表 |
data | Object | 否 | 组件的内部数据,和properties用用于组件的模版渲染 |
observers | Object | 否 | 组件数据字段监听器,用于监听properties和data的变化 |
methods | Object | 否 | 组件的方法,包括事件响应函数和任意的自定义方法 |
created | Function | 否 | 组件生命周期函数,在组件实例刚刚被创建时执行,注意此时不能调用setData |
attached | Function | 否 | 组件生命周期函数,在组件实例进入页面节点树时执行 |
ready | Function | 否 | 组件生命周期函数,在组件布局完成后执行 |
moved | Function | 否 | 组件生命周期函数,在组件实例被移动到节点树另一个位置时执行 |
detached | Function | 否 | 组件生命周期函数,在组件实例被从页面节点树移除时执行 |
五、父组件向子组件传值
直接看代码:
#父组件的wxml文件,在此文件中引入CusComponent组件
//parentData为要传递的数据名称,parent_data为数据内容
#自定义组件的.js文件里的properties属性
properties: {
//要接收的数据名称
parentData:{
//要接收的数据类型
type:String,
//要接收的数据
value:''
}
}
{{item.name}}
{{parentData}}
过程:
1、父组件通过parentData="parent_data"
将数据传到子组件
2、子组件的properties
接收到数据后,根据父组件数据名parentData
,存到parentData
的value
3、子组件的wxml文件
直接使用{{parentData}}
来拿到父组件传递过来的数据。
六、子组件向父组件传值
子组件给父组件传递数据通过自定义组件触发事件来进行传递。先看代码:
#子组件的.js文件
methods: {
handleActive(e){
const {index} = e.currentTarget.dataset;
//triggerEvent("自定义事件名称",要传递的数据),触发事件
this.triggerEvent("itemChange",{index});
}
}
#父组件的wxml文件
< CusComponent itemList="{{itemList}}" binditemChange="changeItemList">
#父组件.js文件中的方法
changeItemList(e){
const index = e.detail.index;
let itemList = this.data.itemList;
itemList.forEach((v,i) => {
i===index?v.isActive=true:v.isActive=false
});
this.setData({
itemList:itemList
})
}
过程:
1、点击子组件后调用子组件的handleActive(e)
,此方法中有个自定义组件触发事件this.triggerEvent("itemChange",{index});
2、触发父组件wxml文件
引用的组件中的
binditemChange
指向的changeItemList()
方法
3、在父组件的.js文件
中的changeItemList()
方法中对子组件传递过来的数据进行处理
- triggerEvent方法参数:triggerEvent('myevent',{myEventDetail},{myEventOption});
triggerEvent方法三个参数解释:
'myevent':自定义事件的名称
myEventDetail:detail对象,提供给事件监听函数,如上面传递的数据
myEventOption:触发事件的选项,不是必填项。
myEventOption的三个选项解释:
bubbles:事件是否冒泡。默认值false
composed:事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部。默认值false
capturePhase:事件是否拥有捕获阶段。默认值false
七、自定义组件中的 slot 标签
组件的写法和页面相同。组件与组件数据结合后生产的节点树,将被插入到组件的引用位置上。
在组件中提供了一个
节点,用于承载组件引用时提供的子节点。
注意:在模板中引用到的自定义组件及其对应的节点名需要在 json
文件中显式定义,否则会被当作一个无意义的节点。除此以外,节点名也可以被声明为抽象节点。
#父组件wxml中引用的子组件
父组件传递过来的节点,相当于子组件中的slot
#子组件wxml中
上面的代码就会让父组件传递过来的节点,相当于子组件中的slot
在你的自定义组件中显示。
需要多个slot
时,如何在组件js中声明:
#子组件的.js文件
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
})
此时就可以在组件的wxml
中使用多个slot
,以不用的name来区分:
#子组件的.wxml文件
八、自定义组件的样式
1、组件样式
组件对应 wxss 文件的样式,只对组件wxml内的节点生效。编写组件样式时,需要注意以下几点:
- 请使用class选择器:组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器。
- 避免使用后代选择器:组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,如遇,请避免使用。
- 子元素选择器只能用于view及子节点:子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。
- 继承样式:继承样式,如 font 、 color ,会从组件外继承到组件内。
- app.wxss样式组件对自定义组件无效:除继承样式外, app.wxss 中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)。
#a { } /* 在组件中不能使用 */
[a] { } /* 在组件中不能使用 */
button { } /* 在组件中不能使用 */
.a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */
除此以外,组件可以指定它所在节点的默认样式,使用 :host
选择器(需要包含基础库 [1.7.2]。
/* 组件 custom-component.wxss */
:host {
color: yellow;
}
2、组件样式隔离
默认情况下,自定义组件的样式只受到自定义组件 wxss 的影响。除非以下两种情况:
- app.wxss 或页面的 wxss 中使用了标签名选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件。通常情况下这是不推荐的做法。
- 指定特殊的样式隔离选项 styleIsolation 。
Component({
options: {
styleIsolation: 'isolated'
}
})
styleIsolation的选项:
isolated
表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);
apply-shared
表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
shared
表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。(这个选项在插件中不可用。)
使用后两者时,请务必注意组件间样式的相互影响。
如果这个Component 构造器用于构造页面,则默认值为 shared
,且还有以下几个额外的样式隔离选项可用:
-
page-isolated
表示在这个页面禁用 app.wxss ,同时,页面的 wxss 不会影响到其他自定义组件; -
page-apply-shared
表示在这个页面禁用 app.wxss ,同时,页面 wxss 样式不会影响到其他自定义组件,但设为shared
的自定义组件会影响到页面; -
page-shared
表示在这个页面禁用 app.wxss ,同时,页面 wxss 样式会影响到其他设为apply-shared
或shared
的自定义组件,也会受到设为shared
的自定义组件的影响
在 Component 的 options
中设置addGlobalClass: true
。 这个选项等价于设置 styleIsolation: apply-shared
,但设置了 styleIsolation
选项后这个选项会失效。
/* 组件 custom-component.js */
Component({
options: {
addGlobalClass: true,
}
})
3、外部样式类、引用页面或父组件的样式、虚拟化组件节点
参见官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html
写在最后:
如果文章中有错误或是表达不准确的地方,欢迎大家评论中指正,以便我完善。
文章我也会根据所学到新的知识不断更新。