②在新建的 components -> test 文件夹上,鼠标右键,点击“新建 Component'
③键入组件的名称之后回车,会自动生成组件对应的 4个文件,后缀名分别为 js,json,.wxml和.wxss
组件的引用方式分为“局部引用”和“全局引用
局部引用:组件只能在当前被引用的页面内使用
全局引用:组件可以在每个小程序页面中使用
在页面的.json文件中引用组件,即为局部引用组件,该组件只能在当前被引用的页面内使用
"usingComponents": {
"my-test":"/components/test/test"
}
<my-test></my-test>
在app.json文件中引用组件,即为全局引用组件,该组件可以在任意页面内使用
代码同上
倘若一个组件需要在多个页面被多次调用,则使用全面引用组件。
倘若一个组件仅在一个页面内被多次调用,则只需使用局部引用组件。
从表面来看,组件和页面都是由.js,.json,.wxml和.wxs 这四个文件组成的。但是,组件和页面的.js与.json 文件有明显的不同:
组件的 .json 文件中需要声明"component": true 属性
组件的.js 文件中调用的是 Component()函数(页面的是Pages()函数)
组件的事件处理函数需要定义到 methods 节点中
默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的Ul 结构
好处:
防止外界的样式影响组件内部的样式
防止组件的样式破坏外界的样式
app.wxss中的全局样式对组件无效
只有class 选择器会有样式隔离效果,id 选择器、属性选择器、标签选择器不受样式隔离的影响
建议:在组件和用组件的页面中建议使用 class 选择器而不要使用 id、属性、标签选择器
默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望在外界能够控制组件内部的样式,此时,可以通过 stylelsolation 修改组件的样式隔离选项,用法如下:
在js文件中:
Component({
options:{
styleIsolation:'isolated'
})
或者在json文件中:
"styleIsolation": "isolated"
类似于页面,在小程序组件中,用于组件模板渲染的私有数据需要定义在data节点中。
在小程序组件中,事件处理函数和自定义方法都需要放在methods节点中,其中自定义函数需以_开头,例:
<button type="primary" bindtap="btp">+1</button>
methods: {
btp(){
this.setData({
count: this.data.count +1
})
this._showCount()
},
_showCount(){
wx.showToast({
title: 'count的值为:'+ this.data.count,
icon:'none'
})
}
}
在小程序组件中,properties 是组件的对外属性,用来接收外界传递到组件中的数据。(在页面的.json文件中引用组件后,才能在对应的.wxml文件中使用该组件)示例代码如下:
//在.js文件中
properties: {
// 简单的定义方式
// max:Number
// 完整的定义方式
max:{
type:Number,
value:10
}
},
//在.json文件中
"usingComponents": {
"my-test":"/components/test/test"
}
//在该.json对应的.wxml文件中
<my-test max="10"></my-test>
在小程序的组件中,properties 属性和 data 数据的用法相同,它们都是可读可写的,只不过:
data 更倾向于存储组件的私有数据
properties 更倾向于存储外界传递到组件中的数据
验证方法:
methods: {
_showInfo(){
console.log(this.data)
console.log(this.properties)
console.log(this.data === this.properties)
}
}
<button type="primary" bindtap="_showInfo">_showInfo</button>
由于 data 数据和 properties 属性在本质上没有任何区别,因此 properties 属性的值也可以用于页面渲染或使用setData为properties 中的属性重新赋值,示例代码如下:
methods: {
btp() {
if (this.data.count >= this.properties.max) return
this.setData({
count: this.data.count + 1,
max:this.properties.max +1
})
console.log(this.properties.max)
this._showCount()
}
}
数据监听器用于监听和响应任何属性和数据字段的变化,从而执行特定的操作。它的作用类似于 vue 中的watch 侦听器。在小程序组件中,数据监听器的基本语法格式如下:
Component({
observers: {
'字段A,字段B':function(字段A的新值,字段B的新值){
//需要处理的事务
}
}
})
例:使用监听器实现n1n2改变后自动改变他们的和
注意在对应的.json文件中进行引用,不然无法显示
Component({
data: {
n1:0,
n2:0,
sum:0
},
ods: {
btp1(){
this.setData({
n1:this.data.n1+1
})
},
btp2(){
this.setData({
n2:this.data.n2+1
})
}
},
observers:{
'n1,n2':function(n1,n2){
this.setData({
sum:this.data.n1 + this.data.n2
})
}
}
})
<view>{{n1}} + {{n2}} = {{sum}}</view>
<button type="primary" bindtap="btp1">n1+</button>
<button type="primary" bindtap="btp2">n2+</button>
数据监听器支持监听对象的单个或多个属性的变化,示例如下:
observers:{
'对象.属性1,对象.属性2':function(属性1的新值,属性2的新值){
// 触发此监听器的 3 种情况:
//[为属性A赋值] 使用 setData 设置 this.data.对象.属性A 时触发
//[为属性B赋值]使用 setData 设置 this.data.对象.属性B 时触发
//[直接为对象赋值]使用 setData 设置 this.data.对象 时触发
// do something...
}
}
纯数据字段指的是不用于界面渲染的data字段
应用场景:例如有些情况下,某些 data 中的字段既不会展示在界面上,也不会传递给其他组件,仅仅在当前组件内部使用。带有这种特性的 data 字段适合被设置为纯数据字段。
好处:提升页面更新的性能
在Component 构造器的 options 节点中,指定 pureDataPattern 为一个正则表达式,字段名符合这个正则表达式的字段将成为纯数据字段,示例代码如下:
Component({
options: {
//指定所有的以_开头的的数据字段为纯数据字段
pureDataPattern: /^_/
},
data: {
a:true, //普通数据字段
_b:true //纯数据字段
}
})
在小程序生命周期当中,最重要的生命周期函数有三个,分别是created、attached、detached。它们各自的特点如下:
①组件实例刚被创建好的时候,created 生命周期函数会被触发
此时还不能调用 setData
通常在这个生命周期函数中,只应该用于给组件的 this 添加一些自定义的属性字段
②在组件完全初始化完毕、进入页面节点树后,
attached 生命周期函数会被触发
此时,this.data 已被初始化完毕
这个生命周期很有用,绝大多数初始化的工作可以在这个时机进行(例如发请求获取初始数据)
③在组件离开页面节点树后, detached 生命周期函数会被触发
退出一个页面时,会触发页面内每个自定义组件的 detached 生命周期函数
此时适合做一些清理性质的工作
在小程序组件中,生命周期函数可以直接定义在 Component 构造器的第一级参数中,可以在 lifetimes 字段内进行声明 (这是推荐的方式,其优先级最高)。示例代码如下:
lifetimes:{
created(){
console.log('created')
},
attached(){
console.log('attached')
}
}
有时,自定义组件的行为依赖于页面状态的变化,此时就需要用到组件所在页面的生命周期。
例如:每当触发页面的 show 生命周期函数的时候,我们希望能够重新生成一个随机的 RGB 颜色值
在自定义组件中,组件所在页面的生命周期函数有如下3个,分别是:
组件所在页面的生命周期函数,需定义在pageLifetimes节点下,例如:
pageLifetimes:{
show:function(){
console.log('show')
},
hide(){
console.log('hide')
},
resize(){
console.log('resize')
}
}
methods: {
_randomNumber(){
this.setData({
_rgb:{
r:Math.floor(Math.random()*256),
g:Math.floor(Math.random()*256),
b:Math.floor(Math.random()*256)
}
})
}
},
pageLifetimes:{
show:function(){
this._randomNumber()
console.log(this.data._rgb)
}}
实现在页面上显示一个颜色,该颜色由r,g,b三个元素组成RGB颜色,同时定义一个三个按键课时其对应的元素每次+5但不超过上限(256),并在页面初始化时随机赋予一个随机值显现出来
<view style="background-color: rgb({{fullcolor}});" class="colorBox">颜色值:{{fullcolor}}</view>
<button size="mini" bindtap="changeR" type="default">R</button>
<button size="mini" bindtap="changeG" type="primary">G</button>
<button size="mini" bindtap="changeB" type="warn">B</button>
.colorBox {
line-height: 200rpx;
font-size: 24rpx;
color: white;
text-shadow: 0rpx 0rpx 2rpx black;
text-align: center;
}
Component({
options: {
pureDataPattern: /^_/
},
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
_rgb:{
r:0,
g:0,
b:0
},
fullcolor:'0,0,0'
},
/**
* 组件的方法列表
*/
methods: {
_randomNumber(){
this.setData({
_rgb:{
r:Math.floor(Math.random()*256),
g:Math.floor(Math.random()*256),
b:Math.floor(Math.random()*256)
}
})
},
changeR(){
this.setData({
'_rgb.r':this.data._rgb.r + 5 > 255 ? 255 : this.data._rgb.r + 5
})
},
changeG(){
this.setData({
'_rgb.g' : this.data._rgb.g + 5 > 255 ? 255 : this.data._rgb.g + 5
})
},
changeB(){
this.setData({
'_rgb.b' : this.data._rgb.b + 5 > 255 ? 255 : this.data._rgb.b + 5
})
},
},
observers:{
'_rgb.**':function(obj){
this.setData({
fullcolor:`${obj.r},${obj.g},${obj.b}`
})
}
},
lifetimes:{
created(){
console.log('created')
},
attached(){
console.log('attached')
}
},
pageLifetimes:{
show:function(){
this._randomNumber()
}}
})
在自定义组件的 wxml结构中,可以提供一个 <slot> 节点 (插槽),用于承载组件使用者提供的 wxml 结构
在小程序中,默认每个自定义组件中只允许使用一个 <slot> 进行占位,这种个数上的限制叫做单个插槽。例:
<view>
<view>这里是第一行文本</view>
<slot></slot>
</view>
<newtest>
<view>这里是插入slot里面的内容</view>
</newtest>
在小程序的自定义组件中,需要使用多 <slot>插槽时,可以在组件的.js文件中,通过在components节点下建立options节点,设置multipleSlots属性为true来进行启用。示例代码如下:
Component({
options: {
multipleSlots:'true'
}
})
<newtest>
<view slot="before">这里是插入slot-before里面的内容</view>
<view slot="after">这里是插入slot-after里面的内容</view>
</newtest>
<view>
<slot name="before"></slot>
<view>这里是第一行文本</view>
<slot name="after"></slot>
</view>
①属性绑定
用于父组件向子组件的指定属性设置数据,仅能设置JSON兼容的数据
②事件绑定
用于子组件向父组件传递数据,可以传递任意数据
③获取组件实例
父组件还可以通过 this.selectComponent() 获取子组件实例对象,这样就可以直接访问子组件的任意数据和方法
<!--在父文件中-->
<newtest count="{{count}}"></newtest>
<view>父组件中,count的值是:{{count}}</view>
Page({
data: {
count:5
}
})
<!--在子文件中-->
<view>子组件中,count值是:{{count}}</view>
<button bindtap="addCount">+1</button>
Component({
behaviors: [myBehavior],
properties: {
count:Number
},
methods: {
addCount() {
this.setData({
count: this.properties.count + 1
})
}
}
})
事件绑定用于实现子向父传值,可以传递任何类型的数据。使用步骤如下:
①在父组件的.js中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件
②在父组件的.wxml中,通过自定义事件的形式,将步骤1中定义的函数引用,传递给子组件
③在子组件的.js中,通过调用 this.triggerEvent('自定义事件名称',{/*参数对象*/}),将数据发送到父组件
④在父组件的.js 中,通过 e.detail 获取到子组件传递过来的数据
案例:
// ①在父组件的,js中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件
syncCount(e){
console.log(e.detail.value)
},
<!-- ②在父组件的.wxml中,通过自定义事件的形式,将步骤1中定义的函数引用,传递给子组件 -->
<newtest count="{{count}}" bind:sync="syncCount"></newtest>
<!-- 第二种写法
<newtest count="{{count}}" bindsync="syncCount"></newtest> -->
<view>父组件中,count的值是:{{count}}</view>
// ③在子组件的.js中,通过调用 this.triggerEvent('自定义事件名称',{/*参数对象*/}),将数据发送到父组件
methods: {
addCount() {
this.setData({
count: this.properties.count + 1
})
this.triggerEvent('sync',{value: this.properties.count})
}
}
//④在父组件的.js 中,通过 e.detail 获取到子组件传递过来的数据
syncCount(e){
this.setData({
count:e.detail.value
})
},
可在父组件里调用 this.selectComponent("id或class选择器"),获取子组件的实例对象,从而直接访问子组件的任意数据和方法。调用时需要传入一个选择器,例如 this.selectComponent(".class")或his.selectComponent("#ID")
<!-- ②在父组件的.wxml中,通过自定义事件的形式,将步骤1中定义的函数引用,传递给子组件 -->
<newtest count="{{count}}" bind:sync="syncCount" class="custom" id="AB"></newtest>
<!-- 第二种写法
<newtest count="{{count}}" bindsync="syncCount"></newtest> -->
<view>父组件中,count的值是:{{count}}</view>
<button bindtap="btp1">点击获取实例对象</button>
btp1(){
const child= this.selectComponent('.custom')
child.setData({
count: child.properties.count +1
})
//也可调用子组件的方法
// child.addCount()
},
behaviors 是小程序中,用于实现组件间代码共享的特性,类似于 Vue.js 中的"mixins".
每个 behavior 可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中。
每个组件可以引用多个 behavior,behavior也可以引用其它behavior。
module.exports = Behavior({
data:{
username:'qihang'
},
properties:{},
methods:{
addCount(){
console.log("addCount")
}
}
})
const mybehavior=require('../../behaviors/my-behavior')
Component({
behaviors:[mybehavior]
})
组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下
如果有同名的属性(properties) 或方法(methods):
1.若组件本身有这个属性或方法,则组件的属性或方法会覆盖 behavior 中的同名属性或方法
2.若组件本身无这个属性或方法,则在组件的 behaviors 字段中定义靠后的 behavior 的属性或方法会覆盖靠前的同名属性或方法;
3.在2的基础上,若存在嵌套引用 behavior 的情况,则规则为: 父behavior 覆盖 子behavior 中的同名属性或方法。
如果有同名的数据字段(data)若同名的数据字段都是对象类型,会进行对象合并
其余情况会进行数据覆盖,覆盖规则为: 组件 > 父behavior > 子behavior > 靠后的 behavior > 靠前的 behavior。(优先级高的覆盖优先级低的,最大的为优先级最高)
生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用:
对于不同的生命周期函数之间,遵循组件生命周期函数的执行顺序
对于同种生命周期函数,遵循如下规则:
behavior 优先于组件执行
子 behavior 优先于 父 behavior 执行
靠前的 behavior 优先于 后的 behavior 执行
如果同一个 behavior 被一个组件多次引用,它定义的生命周期函数只会被执行一次。