创建微信小程序组件
在小程序中创建组件:
1、项目根目录中创建【components】目录,存放自定义组件
2、进入components目录,给组件创建一个组件目录
3、右键组件目录,选择【创建Components】
4、之后回车,会自动生成组件对应的4个文件, 后缀名分别为.js, .json, .wxml和.wxss
一、全局组件和局部组件
局部页面引用组件:
在【页面的.json】配置文件中引用组件的方式,叫做“局部引用”。
示例代码如下:
{
"usingComponents": {
"test1": "/components/test1/test1"
}
}
全局引用组件:
在【app.json】中配置组件信息
"usingComponents": {
"test1": "/components/test1/test1"
}
使用组件:
<!-- 全局可以在任意页面中使用引入的组件, 局部组件只允许在引入的页面中使用 -->
<test1></test1>
组件和页面的区别
从表面来看,组件和页面都是由.js、 json、 .wxml 和.WXSS这四个文件组成的。
但是,组件和页面的js与.json文件有明显的不同:
● 组件的 json文件中需要声明"component": true属性
● 组件的 js文件中调用的是Component()函数
● 组件的事件处理函数需要定义到methods节点中
二、组件之间的样式隔离问题:
一、组件样式隔离
默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的UI结构
● 组件A的样式不会影响组件C的样式
● 组件A的样式不会影响小程序页面的样式
● 小程序页面的样式不会影响组件A和C的样式
好处:
① 防止外界的样式影响组件内部的样式
② 防止组件的样式破坏外界的样式
二、组件样式隔离的注意点
● app.wxss中的全局样式对组件无效
● 只有class选择器会有样式隔离效果,
id选择器、属性选择器、标签选择器不受样式隔离的影响
建议:在组件和引用组件的页面中建议使用class选择器,不要使用id、属性、标签选择器!
三、修改组件的样式隔离选项
默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。
但有时,我们希望在外界能够控制组件内部的样式,
此时,可以通过stylelsolation 修改组件的样式隔离选项,
用法如下:
// components/test1/test1.js
Component({
options: {
styleIsolation: 'isolated'
}
})
// components/test1/test1.json
{
"component": true,
"usingComponents": {},
"styleIsolation": "isolated"
}
四、 stylelsolation的可选值
isolated
表示启用样式隔离,在自定义组件内外,使用class指定的样式将不会相互影响
apply-shared
表示页面WXSs样式将影响到自定义组件,但自定义组件wxSS中指定的样式不会影响页面
shared
表示页面WxSs样式将影响到自定义组件,
自定义组件Wxss中指定的样式也会影响页面和其他设置了apply-shared或shared的自定义组件
三、Data属性 和 Method方法
和页面一样设置
/**
* 组件的初始数据
*/
data: {
count: 0
},
方法是独立的一个对象存放
/**
* 组件的方法列表
* 在这里编写自定义方法,和事件处理函数
* 自定义方法推荐使用下划线前缀标记
* 如:
* addCount() // 事件执行方法
* _showCount() // 自定义方法
*/
methods: {
addCount() {
this.setData({ count: this.data.count + 1})
this._showCount()
},
_showCount() {
wx.showToast({
icon: 'none',
title: `count值:${this.data.count}`
})
}
}
四、Properties属性
/**
* 组件的属性列表
* 用来接收外部页面或者其他组件传递进来的属性
*/
properties: {
// 和vue一样的写法
max: {
type: Number,
value: 10 // 默认值
}
/**
* 简化写法,不设置默认值
* max: Number
*/
},
组件使用时可以设置值
<test1 max="{{5}}"/>
使用propertis数据
等于max值时无法继续叠加
addCount() {
if (this.data.count === this.properties.max) return
this.setData({ count: this.data.count + 1})
this._showCount()
},
五、data和properties的区别
在小程序的组件中,properties 属性和data数据的用法相同,它们都是可读可写的,只不过:
● data更倾向于存储组件的私有数据
● properties更倾向于存储外界传递到组件中的数据
两者的地址一致:
this.data === this.properties // true
给properties属性赋值
// 为properties的数据重新赋值
this.setData({ max: this.properties.max + 1 })
六、数据监听器
监听器功能再页面不支持
/**
* 组件的数据监听器
*/
observers: {
// 可以同时监听多个属性, 入参的参数对应属性个数,表示属性更新后的值
'attr1, attr2': function(newValue1, newValue2) {
// todo ...
}
},
演示案例:
组件HTML代码:
<!--components/test2/test2.wxml-->
<text>components/test2/test2.wxml</text>
<view>
{{n1}} + {{n2}} = {{sum}}
</view>
<button size="mini" type="primary" bindtap="addN1">n1增加</button>
<button size="mini" type="primary" bindtap="addN2">n2增加</button>
组件JS代码:
// components/test2/test2.js
Component({
/**
* 组件的数据监听器
*/
observers: {
'n1, n2': function(n1Value, n2Value) {
this.setData({ sum: n1Value + n2Value })
}
},
/**
* 组件的属性列表
*/
properties: { },
/**
* 组件的初始数据
*/
data: { n1: 0, n2: 0, sum: 0 },
/**
* 组件的方法列表
*/
methods: {
addN1() { this.setData({ n1: this.data.n1 + 1 }) },
addN2() { this.setData({ n2: this.data.n2 + 1 }) }
}
})
演示案例2:
一个RGB颜色案例,HTML代码
<!--components/rgbView/rgbView.wxml-->
<text>components/rgbView/rgbView.wxml</text>
<view class="colorBox" style="background-color: rgb({{fullColor}});">
颜色值:{{fullColor}}
</view>
<button size="mini" bindtap="changeR" >R</button> |
<button size="mini" bindtap="changeG" >G</button> |
<button size="mini" bindtap="changeB" >B</button>
<view>
{{rgb.r}}, {{rgb.g}}, {{rgb.b}}
</view>
JS代码:
// components/rgbView/rgbView.js
Component({
observers: {
// 监听,然后重新赋值
'rgb.**': function(newRgb) {
this.setData({
fullColor: `${newRgb.r}, ${newRgb.g}, ${newRgb.b}`
})
}
},
/**
* 组件的属性列表
*/
properties: { },
/**
* 组件的初始数据
*/
data: {
rgb: { r: 0, g: 0, b: 0 },
fullColor: '0, 0, 0'
},
/**
* 组件的方法列表
*/
methods: {
changeR() {
let rValue = this.data.rgb.r + 5
rValue = rValue > 255 ? 255 : rValue
this.setData({ 'rgb.r': rValue })
},
changeG() {
let gValue = this.data.rgb.g + 5
gValue = gValue > 255 ? 255 : gValue
this.setData({ 'rgb.g': gValue })
},
changeB() {
let bValue = this.data.rgb.b + 5
bValue = bValue > 255 ? 255 : bValue
this.setData({ 'rgb.b': bValue })
}
}
})
RGB显示板样式:
/* components/rgbView/rgbView.wxss */
.colorBox {
line-height: 200rpx;
font-size: 24rpx;
color: white;
text-shadow: 0rpx 0rpx 2rpx black;
text-align: center;
}
七、纯数据字段
1.什么是纯数据字段
概念:纯数据字段指的是那些不用于界面渲染的data字段。
应用场景:例如有些情况下,某些data中的字段既不会展示在界面上,也不会传递给其他组件,仅仅在当前
组件内部使用。带有这种特性的data字段适合被设置为纯数据字段。
好处:纯数据字段有助于提升页面更新的性能。
2.使用规则
在Component构造器的options节点中,指定pureDataPattern为一个正则表达式,
字段名符合这个正则表达式的字段将成为纯数据字段,示例代码如下:
// components/rgbView/rgbView.js
Component({
options: {
// 自定义正则规则来匹配纯数据字段,演示为首字符下划线的属性
pureDataPattern: /^_/
}
}
把之前的JS代码的rgb属性名改为_rgb使用即可:
// components/rgbView/rgbView.js
Component({
options: {
// 自定义正则规则来匹配纯数据字段,演示为首字符下划线的属性
pureDataPattern: /^_/
},
observers: {
// 监听,然后重新赋值
'_rgb.**': function(newRgb) {
this.setData({
fullColor: `${newRgb.r}, ${newRgb.g}, ${newRgb.b}`
})
}
},
/**
* 组件的属性列表
*/
properties: { },
/**
* 组件的初始数据
*/
data: {
_rgb: { r: 0, g: 0, b: 0 },
fullColor: '0, 0, 0'
},
/**
* 组件的方法列表
*/
methods: {
changeR() {
let rValue = this.data._rgb.r + 5
rValue = rValue > 255 ? 255 : rValue
this.setData({ '_rgb.r': rValue })
},
changeG() {
let gValue = this.data._rgb.g + 5
gValue = gValue > 255 ? 255 : gValue
this.setData({ '_rgb.g': gValue })
},
changeB() {
let bValue = this.data._rgb.b + 5
bValue = bValue > 255 ? 255 : bValue
this.setData({ '_rgb.b': bValue })
}
}
})
八、组件的生命周期函数
全部的钩子函数
// components/rgbView/rgbView.js
Component({
lifetimes: {
// 创建完成后执行
created() {},
// 组件实例和页面完成dom绑定后执行,等同vue的mounted
attached() {},
// 组件再页面的视图渲染完成
ready() {},
// 组件被更新后执行(组件实例移动到节点树的新位置)
moved() {},
// 组件从页面的dom树中移除时触发
detached() {},
// 组件发生异常触发
error() {}
}
})
主要的三个函数
2.组件主要的生命周期函数
在小程序组件中,最重要的生命周期函数有3个,分别是created、attached、 detached。 它们各自的特点
如下:
① 组件实例刚被创建好的时候,
created生命周期函数会被触发
● 此时还不能调 用setData
● 通常在这个生命周期函数中, 只应该用于给组件的this添加一些自定义的属性字段
② 在组件完全初始化完毕、
进入页面节点树后,attached 生命周期函数会被触发
此时,this.data 已被初始化完毕
● 这个生命周期很有 用,绝大多数初始化的工作可以在这个时机进行( 例如发请求获取初始数据)
③ 在组件离开页面节点树后,detached生命周期函数会被触发
● 退出一个页面时,会触发页面内每个自定义组件的detached生命周期函数
● 此时适合做一些清理性质的工作
九、所属页面的生命周期函数
1.什么是组件所在页面的生命周期
有时,自定义组件的行为依赖于页面状态的变化,此时就需要用到组件所在页面的生命周期。
// components/rgbView/rgbView.js
Component({
pageLifetimes: {
// 所引用的页面被展示时执行
show() {},
// 所引用的页面被隐藏时执行
hide() {},
// 所引用的页面被尺寸变化时执行, 可以获取size大小
resize(size) {},
}
})
演示案例,在颜色组件中编写一个随机颜色生成的方法:
/**
* 组件的方法列表
*/
methods: {
/**
* 生成随机色彩
*/
_randomColor() {
this.setData({
_rgb: {
r: Math.floor(Math.random() * 256),
g: Math.floor(Math.random() * 256),
b: Math.floor(Math.random() * 256)
}
})
},
}
在页面渲染展示的周期时刻调用:
pageLifetimes: {
// 所引用的页面被展示时执行
show() {
// 在引用页面展示时触发
this._randomColor()
}
}
十、Slot 组件插槽
单个插槽
编写一个组件
<!--components/test4/test4.wxml-->
<text>components/test4/test4.wxml</text>
<view>
<view>这是组件的内部结构</view>
<slot></slot>
</view>
使用组件:
<test4>
<text>这是嵌入组件插槽的部分</text>
</test4>
多个插槽
<view>
<view>这是组件的内部结构1</view>
<slot name="before"></slot>
<view>这是组件的内部结构2</view>
<slot name="after"></slot>
<view>这是组件的内部结构3</view>
</view>
微信新版本需要追加JS配置项:
黑马教程演示没有,说明在老版本可以直接配置
// components/test4/test4.js
Component({
options: {
// 开启多插槽支持
multipleSlots: true,
}
}
使用时需要指明名称:
<test4>
<text slot="before">这是嵌入组件插槽的部分1</text>
<text slot="after">这是嵌入组件插槽的部分2</text>
</test4>
十一、组件之间通信问题
1.父子组件之间通信的3种方式
①属性绑定
●用于父组件向子组件的指定属 性设置数据,仅能设置JSON兼容的数据
②事件绑定
● 用于子组件向父组件传递数据,可以传递任意数据
③获取组件实例
● 父组件 还可以通过this.selectComponent()获取子组件实例对象
● 这样就可以直 接访问子组件的任意数据和方法
一、父组件(页面) 向 子组件传递数据:
<father counter="{{val1}}" />
<view> father组件 counter值: {{val1}}</view>
Father组件接参:
// components/father/father.js
Component({
/**
* 组件的属性列表
*/
properties: {
counter: {
type: Number
}
}
})
二、子组件向父组件传递数据
3.事件绑定
事件绑定用于实现子向父传值,可以传递任何类型的数据。使用步骤如下:
① 在父组件的js中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件
② 在父组件的wxml中,通过自定义事件的形式,将步骤1中定义的函数引用,传递给子组件
③ 在子组件的js中,通过调用this.triggerEvent(自定义事件名称',{/*参数对象*/ }) ,将数据发送到父组件
④ 在父组件的js中,通过e.detail获取到子组件传递过来的数据
1、Father组件先追加事件和展示
<!--components/father/father.wxml-->
<text>components/father/father.wxml</text>
<view>展示Father组件 counter值:{{counter}}</view>
<button bindtap="addCounter">+1</button>
JS代码,使用事件触发器配置事件和传递值:
/**
* 组件的方法列表
*/
methods: {
addCounter() {
this.setData({ counter: this.properties.counter + 1})
// 给父组件进行同步
this.triggerEvent('sync', { value: this.properties.counter})
}
}
页面使用组件时,绑定对应的接收方法:
<father counter="{{val1}}" bind:sync="syncCounter" />
<view> father组件 counter值: {{val1}}</view>
方法编写:
syncCounter(event) {
// 接收从子组件发送过来的参数
this.setData({ val1: event.detail.value })
},
三、直接获取组件实例实现调用
可在父组件里调用this.selectComponent("id或class选择器"),获取子组件的实例对象,
从而直接访问子组件的任意数据和方法。
调用时需要传入一-个选择器,例如this.selectComponent(".my-component")。
<father
counter="{{val1}}"
bind:sync="syncCounter"
class="cf"
id="componentFather" />
<view> father组件 counter值: {{val1}}</view>
<button bindtap="getComponentFather"> 测试</button>
JS代码:
现版本拿不到组件的方法,data属性可以获取
/**
* 直接获取组件实例
*/
getComponentFather() {
// 或者id选择器 this.selectComponent('#componentFather')
// 或者类选择器 this.selectComponent('.cf')
const father = this.selectComponent('.cf')
console.log(father)
console.log(father.data.counter)
// 拿不到组件方法? 清空缓存,刷新页面再尝试
father.addCounter()
},
十二、behaviors
1.什么是behaviors
behaviors是小程序中,用于实现组件间代码共享的特性,类似于Vue.js中的"mixins"
2. behaviors的工作方式
每个behavior可以包含一组属性、 数据、生命周期函数和方法。
组件引用它时,它的属性、数据和方法会被合并到组件中。
每个组件可以引用多个behavior, behavior 也可以引用其它behavior。
3.创建behavior
调用Behavior(Object object)方法即可创建一一个共享 的behavior实例对象,供所有的组件使用:
// /behaviors/bhvirs1.js
module.exports = Behavior({
// 属性
properties: {},
// 数据
data: { msg: 'Hello Behaviors!'},
// 方法
methods: {
}
})
4.导入并使用behavior
在组件中,使用require()方法导入需要的behavior,
挂载后即可访问behavior中的数据或方法,
示例代码如下:
// components/father/father.js
const b1 = require('../../behaviors/bhvirs1')
Component({
behaviors: [b1],
})
组件可以展示Behaviors中的值
<view>展示Behvior数据 {{msg}}</view>
对应的所有设置属性:
冲突问题,见官方文档描述
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html