自定义组件
1. 创建-注册-使用组件
组件介绍
小程序目前已经支持组件化开发,可以将页面中的功能模块抽取成自定义组件,以便在不同的页面中重复使用;
也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。
开发中常见的组件有两种:
- 公共组件:将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用
- 页面组件:将复杂的页面拆分成多个低耦合的模块,有助于代码维护
如果是公共组件,建议将其放在小程序的目录下的 components
文件夹中
如果是页面组件,建议将其放在小程序对应页面目录下,当然你也可以放到页面的 components
文件夹中
同时建议:一个组件一个文件夹,文件夹名称和组件名称保持一致
📌 注意事项:
- 自定义组件的需要在
json
文件中需要配置component
字段设为true
- 自定义组件通过
Component
构造器进行构建,在构造器中可以指定组件的属性、数据、方法等
创建自定义组件:
创建组件的步骤很简单,以公共组件为例,创建的步骤如下:
-
在小程序的目录下新建
components
文件夹 -
在
components
文件夹上,点击右键,选择新建文件夹
,然后输入文件夹名称,我们建议文件夹的名称和组件的名称保持一致,这样方便后期对组件进行维护。我们这里新的的组件名称叫做:custom-checkbox
-
在新建的组件文件夹上,点击右键,选择
新建 Component
,然后输入组件的名称,组件的名称建议和文件夹保持一致 -
此时就已经创建了一个功能组件
使用自定义组件
开发中常见的组件主要分为 公共组件 和 页面组件 两种,因此注册组件的方式也分为两种:
- 全局注册:在
app.json
文件中配置usingComponents
节点进行引用声明,注册后可在任意组件使用 - 局部注册:在页面的
json
文件中配置usingComponents
节点进行引用声明,只可在当前页面使用
在配置 usingComponents
节点进行引用声明时,需要提供自定义组件的标签名和对应的自定义组件文件路径,语法如下:
json
{
"usingComponents": {
"自定义组件的标签名": "自定义组件文件路径"
}
}
这样,在页面的 wxml
中就可以像使用基础组件一样使用自定义组件。节点名即自定义组件的标签名,节点属性即传递给组件的属性值。
json
{
"usingComponents": {
"custom-checkbox": "/components/custom-checkbox/custom-checkbox"
}
}
html
<!--pages/index/index.wxml-->
<view>
<!-- 将导入的自定义组件当成标签使用 -->
<custom-checkbox
/>
</view>
2. 自定义组件-数据和方法
在组件的 .js 中,需要调用 Component
方法创建自定义组件,Component
中有以下两个属性:
data
数据:组件的内部数据
methods
方法:在组件中事件处理程序需要写到 methods
中才可以
落地代码:
➡️ components/custom-checkbox/custom-checkbox.wxml
html
<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->
<view class="custom-checkbox-container">
<view class="custom-checkbox-box">
<checkbox checked="{{ isChecked }}" bindtap="updateChecked" />
</view>
</view>
➡️ components/custom-checkbox/custom-checkbox.wxss
css
/* components/custom-checkbox/custom-checkbox.wxss */
.custom-checkbox-container {
display: inline-block;
}
➡️ components/custom-checkbox/custom-checkbox.js
js
Component({
/**
* 组件的初始数据:用来定义当前组件内部所需要使用的数据
*/
data: {
isChecked: false
},
/**
* 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
*/
methods: {
// 更新复选框的状态
updateChecked () {
this.setData({
isChecked: !this.data.isChecked
})
console.log(this.data.isChecked)
}
}
})
➡️ app.json
json
{
"usingComponents": {
"custom-checkbox": "./components/custom-checkbox/custom-checkbox"
}
}
➡️ index.wxml
html
<custom-checkbox />
<view class="line"></view>
<custom-checkbox />
3. 自定义组件-属性
属性 Properties 是指组件的对外属性,主要用来接收组件使用者传递给组件内部的数据,和 data 一同用于组件的模板渲染
📌 注意事项:
- 设置属性类型需要使用 type 属性,属性类型是必填项,value 属性为默认值
- 属性类型可以为 String、Number、Boolean、Object、Array ,也可以为 null 表示不限制类型
落地代码:
➡️ index.wxml
html
<!-- label 文本显示的内容 -->
<!-- position 控制文本显示的位置 -->
<custom-checkbox label="我已阅读并同意 用户协议 和 隐私协议" position="right" />
<view class="line"></view>
<custom-checkbox label="匿名提交" position="left" />
➡️ components/custom-checkbox/custom-checkbox.wxml
html
<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->
<view class="custom-checkbox-container">
+ <view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
+ <checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />
+
+ <view>
+ <text>{{ label }}</text>
+ </view>
</view>
</view>
➡️ components/custom-checkbox/custom-checkbox.wxss
css
/* components/custom-checkbox/custom-checkbox.wxss */
.custom-checkbox-container {
display: inline-block;
}
.custom-checkbox-box {
display: flex;
align-items: center;
}
.custom-checkbox-box.left {
flex-direction: row-reverse;
}
.custom-checkbox-box.right {
flex-direction: row;
}
.custom-checkbox {
margin-left: 10rpx;
}
➡️ components/custom-checkbox/custom-checkbox.js
js
Component({
+ /**
+ * 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据
+ */
+ properties: {
+ // 如果需要接收传递的属性,有两种方式:全写、简写
+ // label: String
+
+ label: {
+ // type 组件使用者传递的数据类型
+ // 数据类型:String、Number、Boolean、Object、Array
+ // 也可以设置为 null,表示不限制类型
+ type: String,
+ value: ''
+ },
+
+ // 文字显示位置
+ position: {
+ type: String,
+ value: 'right'
+ }
+ },
/**
* 组件的初始数据:用来定义当前组件内部所需要使用的数据
*/
data: {
isChecked: false
},
/**
* 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
*/
methods: {
// 更新复选框的状态
updateChecked () {
this.setData({
isChecked: !this.data.isChecked,
+ // label: '在组件内部也可以修改 properties 中的数据'
})
+ // 在 JS 中可以访问和获取 properties 中的数据
+ // 但是一般情况下,不建议修改,因为会造成数据流的混乱
+ // console.log(this.properties.label)
// console.log(this.data.isChecked)
}
}
})
4. 组件 wxml 的 slot
在使用基础组件时,可以给组件传递子节点传递内容,从而将内容展示到页面中,自定义组件也可以接收子节点内容
只不过在组件模板中需要定义 <slot />
节点,用于承载组件引用时提供的子节点
默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。
同时需要给 slot 添加 name 来区分不同的 slot,给子节点内容添加 slot 属性来将节点插入到 对应的 slot 中
知识点讲解:
➡️ custom01.html
html
<view>
<slot name="slot-top" />
<!-- slot 就是用来接收、承载子节点内容 -->
<!-- slot 只是一个占位符,子节点内容会将 slot 进行替换 -->
<!-- 默认插槽 -->
<view><slot /></view>
<slot name="slot-bottom" />
</view>
➡️ custom01.js
js
// components/custom01/custom01.js
Component({
options: {
// 启用多 slot 支持
multipleSlots: true
}
})
➡️ cart.wxml
html
<custom01>
<text slot="slot-top">我需要显示到顶部</text>
<!-- 默认情况下,自定义组件的子节点内容不会进行展示 -->
<!-- 如果想内容进行展示,需要再组件模板中定义 slot 节点 -->
我是子节点内容
<text slot="slot-bottom">我需要显示到低部</text>
</custom01>
完善复选框案例
➡️ custom-checkbox.html
html
<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->
<view class="custom-checkbox-container">
<view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
<checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />
+ <view>
+ <!-- lable 和 子节点内容都进行了展示 -->
+ <!-- 要么展示 lable 要么展示 子节点内容 -->
+ <!-- 如果用户传递了 lable 属性,就展示 lable -->
+ <!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
+ <text wx:if="{{ label !== '' }}">{{ label }}</text>
+
+ <slot wx:else />
+ </view>
</view>
</view>
➡️ index.html
html
<!-- label 文本显示的内容 -->
<!-- position 控制文本显示的位置 -->
<custom-checkbox label="我已阅读并同意 用户协议 和 隐私协议" position="right">
我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>
<view class="line"></view>
<custom-checkbox label="匿名提交" position="left">
匿名提交 - 222
</custom-checkbox>
5. 组件样式以及注意事项
选择器使用注意事项:
类似于页面,自定义组件拥有自己的 wxss
样式,组件对应 wxss
文件的样式,只对组件wxml内的节点生效。
编写组件样式时,需要注意以下几点:
-
app.wxss 或页面的 wxss 中使用了标签名(view)选择器(或一些其他特殊选择器)来直接指定样式
这些选择器会影响到页面和全部组件,通常情况下这是不推荐的做法
-
组件和引用组件的页面不能使用 id 选择器(#a)、属性选择器([a]) 和 标签名选择器,请改用 class 选择器
-
组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,如遇,请避免使用
-
子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。
-
继承样式,如 font 、 color ,会从组件外继承到组件内。
-
除继承样式外, 全局中的样式、组件所在页面的的样式对自定义组件无效 (除非更改组件样式隔离选项)
css
#a { } /* 在组件中不能使用 */
[a] { } /* 在组件中不能使用 */
button { } /* 在组件中不能使用 */
.a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */
落地代码:
➡️ custom02.wxml
html
<text id="content" class="content son">
<text class="label">给自定义组件设置样式</text>
</text>
➡️ custom02.wxss
scss
/* components/custom02/custom02.wxss */
/* 第一个注意事项:在自定义的 wxss 文件中,不允许使用标签选择器,ID 选择器,属性选择器 */
/* 请改为 class 选择器 */
/* text {
color: lightseagreen;
} */
/* #content {
color: lightseagreen;
} */
/* [id=content] {
color: lightseagreen;
} */
/* .content {
color: lightseagreen;
} */
/* 第二个注意事项:子选择器,只能用于 view 和 子组件,用于其他组件可能会出现样式失效的问题 */
/* .content > .label {
color: lightseagreen;
} */
/* 第三个注意事项:继承样式,例如:color\font 都会从组件外继承 */
/* 第四个注意事项:全局样式、组件所在页面的样式文件中的样式都对自定义组件无效 */
/* 第五个注意事项:官方不推荐做法 */
/* 不建议在 全局样式文件 以及 父级页面之间使用标签选择器设置样式 */
/* 如果是在全局样式文件中设置样式,会影响项目中全部的相同组件 */
/* 如果是再页面样式文件中设置样式,会影响当前页面所有的相同组件 */
/* 第六个注意事项: */
/* 组件和组件使用者,如果使用了后代选择器,可能会出现一些非预期情况 */
/* 如果出现,请避免 */
➡️ cate.wxml
html
<view class="custom parent">
<view>
<custom02 />
<view class="son test">我是父级页面中的结构</view>
</view>
</view>
➡️ cate.wxss
scss
/* pages/cate/cate.wxss */
/* .custom {
color: lightseagreen;
font-size: 50rpx;
} */
/* .label {
color: lightseagreen;
} */
/* text {
color: lightseagreen;
} */
.parent .son.test {
color: lightsalmon;
}
➡️ app.wxss
scss
/* .label {
color: lightseagreen;
} */
/* text {
color: lightseagreen;
} */
6. 组件样式隔离
默认情况下,自定义组件的样式只受到自定义组件 wxss 的影响。除非以下两种情况:
-
app.wxss
或页面的wxss
中使用了标签名(view)选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件。通常情况下这是不推荐的做法。 -
指定特殊的样式隔离选项
styleIsolation
jsComponent({ options: { styleIsolation: 'isolated' } })
styleIsolation
选项它支持以下取值:
isolated
表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);apply-shared
表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;shared
表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了apply-shared
或shared
的自定义组件。
落地代码:
➡️ custom03.wxml
html
<!--components/custom03/custom03.wxml-->
<text class="label">演示组件样式隔离</text>
➡️ custom03.wxss
scss
/* components/custom03/custom03.wxss */
.test {
color: lightseagreen;
font-size: 50rpx;
}
➡️ custom03.js
js
// components/custom03/custom03.js
Component({
options: {
// styleIsolation:配置组件样式隔离
// isolated:开启样式隔离,默认值
// 在默认情况下,自定义组件和组件使用者如果存在相同的类名,类名不会相互影响
// apply-shared:表示组件使用者、页面的 wxss 样式能够影响到自定义组件
// 但是自定义组件的样式不会影响组件使用者、页面的 wxss 样式
// styleIsolation: "apply-shared"
// shared:表示组件使用者、页面的 wxss 样式能够影响到自定义组件
// 自定义组件的样式会影响组件使用者、页面的 wxss 样式
// 和其他使用了 apply-share 以及 share 属性的自定义组件
styleIsolation: 'shared'
}
})
➡️ cate.wxml
html
<custom03 />
➡️ cate.wxss
scss
.label {
color: lightsalmon;
}
7. 拓展-小程序修改checkbox样式
知识点:
技巧:在官方文档,找到官方提供的案例,审查元素,就能看到对应的类名
📌 注意事项
- .custom-checkbox .wx-checkbox-input {}:复选框没有选中时默认的样式
- .custom-checkbox .wx-checkbox-input-checked {}: 复选框选中时默认的样式
- .custom-checkbox .wx-checkbox-input.wx-checkbox-input-checked:before {}:复选框选中时 √ 样式
这几个类名,在全局样式文件、页面样式文件都可以对修改复选框样式,
但是在自定义组件内部使用的时候,需要添加 styleIsolation: 'shared' 属性
落地代码:
➡️ components/custom-checkbox/custom-checkbox.wxss
scss
/* 复选框组件是公共组件 */
/* 以后需要再多个页面或者需要再多个项目中进行使用 */
/* 所以呢,需要先给复选框组件准备、设置一些默认样式 */
/* 如果在其他页面或者其他项目中使用的时候,发现样式不符合产品需求 */
/* 可以进行修改、对默认的样式进行修改 */
/* 1. 需要给复选框设置默认样式 */
/* 需要先找到小程序给复选框提供的类名,通过小程序给提供的类名修改才可以 */
/* 需要先打开小程序开发文档,找到复选框文档,审查元素,进行查找 */
/* 在自定义组件中,不能直接修改复选框样式 */
/* 如果需要进行修改,需要设置 styleIsolation 才可以 */
/* shared:修改其他页面的样式、组件使用者的样式、以及其他使用了 share 以及 apply-share 的组件 */
/* 这时候,不是想要的结果 */
/* 需求是:只想影响当前组件,可以添加命名空间 */
/* 复选框没有选中时默认的样式 */
.custom-checkbox .wx-checkbox-input {
width: 24rpx !important;
height: 24rpx !important;
border-radius: 50% !important;
border: 1px solid #fda007 !important;
margin-top: -6rpx;
}
/* 复选框选中时默认的样式 */
.custom-checkbox .wx-checkbox-input-checked {
background-color: #fda007 !important;
}
/* 复选框选中时 √ 样式 */
.custom-checkbox .wx-checkbox-input.wx-checkbox-input-checked:before {
font-size: 22rpx;
color: #fff;
}
/* 2. 组件使用者也能够修改默认的样式 */
➡️ components/custom-checkbox/custom-checkbox.js
js
Component({
options: {
styleIsolation: 'shared'
}
})
➡️ index.wxss
scss
/* 组件使用者修改复选框的样式 */
.custom .custom-checkbox .wx-checkbox-input {
border: 1px solid lightseagreen !important;
}
.custom .custom-checkbox .wx-checkbox-input-checked {
background-color: lightseagreen !important;
}
8. 数据监听器
知识点:
数据监听器可以用于监听和响应任何属性和数据字段的变化,有时,需要在一些数据字段被 setData 设置时,需要执行一些操作。那么就可以使用 observers
数据监听器来实现。语法如下:
js
Component({
data: {
num: 10,
count: 1,
obj: { name: 'Tom', age: 10 },
arr: [1, 2, 3]
},
observers: {
// key 是需要检测数据
// value 是一个函数,函数接收一个形参作为参数,是最新的值
num: function(newNum) {
console.log(newNum)
},
// 数据监听器支持监听属性或内部数据的变化,可以同时监听多个
'num, count': function (newNum, newCount) {
console.log(newNum, newCount)
}
// 监听器可以监听子数据字段
'obj.age': function(newAge) {
console.log(newAge)
},
// 如果需要监听所有子数据字段的变化,可以使用通配符 **
'obj.**': function(newAge) {
console.log(newAge)
},
'arr[0]': function (val) {}
}
})
9. 组件间通信与事件
9.1 父往子传值
知识点:
父组件如果需要向子组件传递指定属性的数据,在 WXML 中需要使用数据绑定的方式
与普通的 WXML 模板类似,使用数据绑定,这样就可以向子组件的属性传递动态数据。
父组件如果需要向子组件传递数据,只需要两个步骤:
1.在父组件 WXML 中使用 数据绑定 的方式向子组件传递动态数据
2.子组件内部使用 properties 接收父组件传递的数据即可
知识点代码:
html
<!-- 引用组件的页面模板 -->
<view>
<costom prop-a="{{ name }}" prop-b="{{ age }}" />
</view>
在组件内部,需要在 Component
构造器中通过 properties
接收传递的数据,接收方式有两种:
js
Component({
/**
* 组件的属性列表 props
*/
properties: {
propA: {
type: String, // 传递的数据类型
value: '' // 默认值
},
propB: Number // 简化的定义方式
},
// coding...
})
在子组件中也可以通过 this.setData()
对 properties
中的数据进行修改,但是一般不建议修改
js
// components/custom01/custom01.js
Component({
/**
* 组件的方法列表
*/
methods: {
// 修改列表中的数据
updateProp () {
this.setData({
propB: this.properties.propB + 1
})
}
}
})
复选框组件案例:
➡️ index.js
js
Page({
data: {
isChecked: true
},
// coding...
})
➡️ index.wxml
html
<custom-checkbox
label="我已阅读并同意 用户协议 和 隐私协议"
position="right"
+ checked="{{ isChecked }}">
我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>
➡️ components/custom-checkbox/custom-checkbox.js
js
Component({
options: {
styleIsolation: 'shared'
},
properties: {
// coding...
// 复选框组件公共组件
// 需要再多个页面、在多个项目中进行使用
// 在使用的时候,有的地方希望默认是选中的效果,有的地方希望默认是没有被选中的效果
// 怎么处理 ?
// 首先让复选框默认还是没有被选中的效果
// 如果希望复选框默认被选中,这时候传递属性(checked=true)到复选框组件
+ checked: {
+ type: Boolean,
+ value: false
+ }
},
/**
* 组件的初始数据:用来定义当前组件内部所需要使用的数据
*/
data: {
isChecked: false
},
+ observers: {
+ // 如果需要将 properties 中的数据赋值给 data
+ // 可以使用 observers 进行处理
+ checked: function (newChecked) {
+ // console.log(newChecked)
+ this.setData({
+ isChecked: newChecked
+ })
+ }
+ },
/**
* 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
*/
methods: {
// 更新复选框的状态
updateChecked () {
this.setData({
+ isChecked: !this.data.isChecked,
+ // checked: !this.properties.checked
// label: '在组件内部也可以修改 properties 中的数据'
})
// 在 JS 中可以访问和获取 properties 中的数据
// 但是一般情况下,不建议修改,因为会造成数据流的混乱
// console.log(this.properties.label)
// console.log(this.data.isChecked)
}
}
})
➡️ components/custom-checkbox/custom-checkbox.wxml
html
<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->
<view class="custom-checkbox-container">
<view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
+ <checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />
<view class="content">
<!-- lable 和 子节点内容都进行了展示 -->
<!-- 要么展示 lable 要么展示 子节点内容 -->
<!-- 如果用户传递了 lable 属性,就展示 lable -->
<!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
<text wx:if="{{ label !== '' }}">{{ label }}</text>
<slot wx:else />
</view>
</view>
</view>
9.2 子往父传值
子组件如果需要向父组件传递数据,可以通过小程序提供的事件系统实现传递传递,可以传递任意数据。
事件系统是组件间通信的主要方式之一,自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件,流程如下:
- 自定义组件触发事件时,需要使用
triggerEvent
方法发射一个自定义的事件 - 自定义组件标签上通过 bind 方法监听发射的事件
触发事件:
html
<!-- 在自定义组件中 -->
<button type="primary" plain bindtap="sendData">传递数据</button>
js
// components/custom05/custom05.js
Component({
// 组件的初始数据
data: {
num: 666
},
// 组件的方法列表
methods: {
// 将数据传递给父组件
sendData () {
// 如果需要将数据传递给父组件
// 需要使用 triggerEvent 发射自定义事件
// 第二个参数,是携带的参数
this.triggerEvent('myevent', this.data.num)
}
}
})
监听事件:
html
<view>{{ num }}</view>
<!-- 需要在自定义组件标签上通过 bind 方法绑定自定义事件,同时绑定事件处理函数 -->
<custom05 bind:myevent="getData" />
js
Page({
data: {
num: ''
},
getData (event) {
// 可以通过事件对象.detail 获取子组件传递给父组件的数据
// console.log(event)
this.setData({
num: event.detail
})
}
})
复选框组件案例:
➡️ components/custom-checkbox/custom-checkbox.js
js
Component({
/**
* 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
*/
methods: {
// 更新复选框的状态
updateChecked () {
this.setData({
isChecked: !this.data.isChecked,
// label: '在组件内部也可以修改 properties 中的数据'
})
// 在 JS 中可以访问和获取 properties 中的数据
// 但是一般情况下,不建议修改,因为会造成数据流的混乱
// console.log(this.properties.label)
// console.log(this.data.isChecked)
+ // 目前复选框组件的状态是存储在复选框组件内部的、存储在自定义组件内部的
+ // 但是,在以后实际开发中,组件使用者、父组件有时候也需要获取到复选框内部的状态
+ // 怎么办 ?
+ // 这时候,自定义组件内部就需要发射一个自定义事件,
+ // 如果组件使用者、父组件需要使用数据,绑定自定义事件进行获取即可
+ this.triggerEvent('changechecked', this.data.isChecked)
}
}
})
➡️ index.html
html
<custom-checkbox
label="我已阅读并同意 用户协议 和 隐私协议"
position="right"
checked="{{ isChecked }}"
class="getchild"
+ bind:changechecked="getData"
>
我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>
➡️ index.js
js
Page({
data: {
isChecked: true
},
getData (event) {
console.log(event.detail)
if (event.detail) {
console.log('提交')
} else {
console.log('请同意协议!')
}
}
})
9.3 获取组件实例
如果前面两种方式不足以满足需要。
可在父组件里调用 this.selectComponent()
,获取子组件的实例对象,就可以直接拿到子组件的任意数据和方法。调用时需要传入一个匹配选择器 selector
,如:this.selectComponent(".my-component")
。
html
<!-- 父组件 -->
<costom bind:myevent="getData" class="custom" />
<button bindtap="getChildComponent"></button>
js
// 父组件
Page({
data: {},
getChildComponent: function () {
const child = this.selectComponent('.custom')
console.log(child)
}
})
复选框组件案例:
➡️ index.html
html
<custom-checkbox
label="我已阅读并同意 用户协议 和 隐私协议"
position="right"
checked="{{ isChecked }}"
+ class="child"
+ id="child"
bind:changechecked="getData"
>
我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>
<button type="primary" plain bindtap="getChild">获取子组件实例对象</button>
➡️ index.js
js
Page({
// coding...
// 获取子组件的实例对象
getChild () {
// this.selectComponent 方法获取子组件实例对象
// 获取到实例对象以后,就能获取子组件所有的数据、也能调用子组件的方法
const res = this.selectComponent('#child')
console.log(res.data.isChecked)
}
})
10. 组件生命周期
组件的生命周期:指的是组件自身的一些钩子函数,这些函数在特定的时间节点时被自动触发
组件的生命周期函数需要在 lifetimes
字段内进行声明
最重要的生命周期是 created
attached
detached
包含一个组件生命周期流程的最主要时间点
定义段 | 描述 |
---|---|
created |
在组件实例刚刚被创建时执行,注意此时不能调用 setData (还没有对模板解析) |
attached |
在组件实例进入页面节点树时执行 (模板已经解析完毕,并且挂载到页面上) |
ready | 在组件布局完成后执行 |
moved | 在组件实例被移动到节点树另一个位置时执行 |
detached |
在组件实例被从页面节点树移除时执行 (组件被销毁了) |
-
【组件实例刚刚被创建好时】,
created
生命周期被触发。此时,组件数据this.data
就是在Component
构造器中定义的数据data
。 此时还不能调用setData
。 通常情况下,这个生命周期只应该用于给组件this
添加一些自定义属性字段。 -
【在组件完全初始化完毕】、进入页面节点树后,
attached
生命周期被触发。此时,this.data
已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。 -
【在组件离开页面节点树后】,
detached
生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则detached
会被触发。
js
Component({
lifetimes: {
created: function () {
// 在组件实例刚刚被创建时执行,注意此时不能调用 setData
// 一般用来为组件添加一些自定义属性字段。
},
attached: function() {
// attached 在组件完全初始化完毕、进入页面节点树后执行
// 模板已经解析完毕,并且挂载到页面上
// 一般都是在这里写对应的交互
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
// coding...
}
// coding...
})
11. 组件所在页面的生命周期
组件还有一些特殊的生命周期,这类生命周期和组件没有很强的关联
主要用于组件内部监听父组件的展示、隐藏状态,从而方便组件内部执行一些业务逻辑的处理
组件所在页面的生命周期有 4 个: show、 hide、 resize、 routeDone,需要在 pageLifetimes
字段内进行声明
js
// components/custom06/custom06.js
Component({
// coding...
// 组件所在页面的生命周期
pageLifetimes: {
// 监听组件所在的页面展示(后台切前台)状态
show () {
console.log('组件所在的页面被展示')
},
// 监听组件所在的页面隐藏(前台切后台、点击 tabBar)状态
hide () {
console.log('组件所在的页面被隐藏')
}
}
})
12. 小程序生命周期总结
小程序冷启动,钩子函数执行的顺序
保留当前页面(navigate) 以及 关闭当前页面(redirect)
切后台 以及 切前台(热启动)
13. 拓展:使用 Component 构造页面
Component 方法用于创建自定义组件
小程序的页面也可以视为自定义组件,因此页面也可以使用 Component 方法进行创建,从而实现复杂的页面逻辑开发
📌 注意事项:
要求对应 json 文件中包含 usingComponents 定义段
页面使用 Component 构造器创建,需要定义与普通组件一样的字段与实例方法
页面 Page 中的一些生命周期方法(如 onLoad() 等以"on"开头的方法),在 Component 中要写在 methods 属性中才能生效
组件的属性 Properties 可以用于接收页面的参数,在 onLoad() 中可以通过 this.data 拿到对应的页面参数
落地代码:
js
Component({
// 为什么需要使用 Component 方法进行构造页面
// Component 方法功能比 Page 方法强大很多
// 如果使用 Component 方法构造页面,可以实现更加复杂的页面逻辑开发
// 小程序页面也可以使用 Component 方法进行构造
// 注意事项:
// 1. 要求 .json 文件中必须包含 usingComponents 字段
// 2. 里面的配置项需要和 Component 中的配置项保持一致
// 3. 页面中 Page 方法有一些钩子函数、事件监听方法,这些钩子函数、事件监听方法必须方法 methods 对象中
// 4. 组件的属性 properties 也可以接受页面的参数,在 onLoad 钩子函数中可以通过 this.data 进行获取
properties: {
id: String,
title: String
},
data: {
name: 'tom'
},
// onLoad () {
// console.log('页面加载 - 1')
// },
methods: {
// 更新 name
updateName() {
this.setData({
name: 'jerry'
})
},
onLoad (options) {
// console.log('页面加载 - 2')
// console.log(options)
console.log(this.data.id)
console.log(this.data.title)
console.log(this.properties.id)
},
}
})
14. 拓展:behaviors
小程序的 behaviors 方法是一种代码复用的方式,可以将一些通用的逻辑和方法提取出来,然后在多个组件中复用,从而减少代码冗余,提高代码的可维护性。
如果需要 behavior 复用代码,需要使用 Behavior() 方法,每个 behavior 可以包含一组属性、数据、生命周期函数和方法
组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。
注册 behavior:
如果需要注册一个 behavior
,需要借助 Behavior()
方法,接受一个 Object
类型的参数
js
// my-behavior.js
module.exports = Behavior({
behaviors: [],
properties: {
myBehaviorProperty: {
type: String
}
},
data: {
myBehaviorData: 'my-behavior-data'
},
created: function () {
console.log('[my-behavior] created')
},
attached: function () {
console.log('[my-behavior] attached')
},
ready: function () {
console.log('[my-behavior] ready')
},
methods: {
myBehaviorMethod: function () {
console.log('[my-behavior] log by myBehaviorMehtod')
},
}
})
使用 behavior:
js
// my-component.js
const myBehavior = require('my-behavior')
Component({
behaviors: [myBehavior]
// coding...
})
组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下:
-
如果有同名的属性或方法,采用 "就近原则",组件会覆盖 behavior 中的同名属性或方法
-
如果有同名的数据字段且都是对象类型,会进行对象合并,其余情况会 采用 "就近原则" 进行数据覆盖
-
生命周期函数和 observers 不会相互覆盖,会是在对应触发时机被逐个调用,也就是都会被执行
详细的规则:同名字段的覆盖和组合规则
15. 拓展:外部样式类
默认情况下,组件和组件使用者之间如果存在相同的类名不会相互影响,组件使用者如果想修改组件的样式,需要就解除样式隔离,但是解除样式隔离以后,在极端情况下,会产生样式冲突、CSS 嵌套太深等问题,从而给我们的开发带来一定的麻烦。
外部样式类:在使用组件时,组件使用者可以给组件传入 CSS 类名,通过传入的类名修改组件的样式。
如果需要使用外部样式类修改组件的样式,在 Component 中需要用 externalClasses 定义若干个外部样式类。
外部样式类的使用步骤:
1.在 Component 中用 externalClasses 定义段定义若干个外部样式类
2.自定义组件标签通过 属性绑定 的方式提供一个样式类,属性是 externalClasses 定义的元素,属性值是传递的类名
3.将接受到的样式类用于自定义组件内部
📌注意事项:
在同一个节点上使用普通样式类和外部样式类时,两个类的优先级是未定义的
因此需要添加 !important 以保证外部样式类的优先级
落地代码:
➡️ custom09.js
js
// components/custom09/custom09.js
Component({
// 组件接受的外部样式类
externalClasses: ['extend-class']
})
➡️ custom09.wxml
html
<!-- 在同一个节点上,如果存在外部样式类 和 普通的样式类 -->
<!-- 两个类的优先级是未定义的 -->
<!-- 建议:在使用外部样式类的时,样式需要通过 !important 添加权重 -->
<view class="extend-class box">通过外部样式类修改组件的样式</view>
➡️ custom09.wxss
scss
.box {
color: lightseagreen;
}
➡️ profile.wxml
html
<!-- 属性是在 externalClasses 里面定义的元素 -->
<!-- 属性值必须是一个类名 -->
<custom09 extend-class="my-class" />
➡️ profile.wxss
scss
/* pages/index/index.wxss */
.my-class {
color: lightsalmon !important;
}
16. 完善复选框案例并总结自定义组件
总结自定义组件:
-
组件基本使用:数据、属性、方法、插槽
-
组件样式使用:组件样式、注意事项、样式隔离、外部样式类
-
组件通信传值:父往子传值、子往父传值、获取组件实例
-
组件生命周期:组件的生命周期、组件所在页面的生命周期、总结了小程序全部的生命周期
-
组件数据监听器:observers
-
组件拓展:使用 Component 构造页面、组件复用机制 behaviors 等
完善复选框案例
➡️ components/custom-checkbox/custom-checkbox.wxml
html
<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->
<view class="custom-checkbox-container">
<view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
+ <label class="custom-label">
<checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />
<view class="content">
<!-- lable 和 子节点内容都进行了展示 -->
<!-- 要么展示 lable 要么展示 子节点内容 -->
<!-- 如果用户传递了 lable 属性,就展示 lable -->
<!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
<text wx:if="{{ label !== '' }}">{{ label }}</text>
<slot wx:else />
</view>
+ </label>
</view>
</view>
➡️ components/custom-checkbox/custom-checkbox.wxss
scss
+ .custom-checkbox-box .custom-label {
display: flex;
align-items: center;
}