微信小程序自定义组件
自定义组件
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
创建自定义组件
类似页面 , 一个自定义组件 由 json wxml wxss js 4个文件组成
- 在根目录下 , 新建 components 文件夹 用来存放组件 , 在components 文件夹下新建Tabs 文件夹, 点击Tabs文件夹 ,右击选择 新建Component ,将会自动生成 json wxml wxss js 4个文件
注意: 如果不先新建 Tabs 文件, 直接右击创建 Component , 就不会为json wxml wxss js 4个文件生成 Tabs 父文件夹
声明自定义组件
在要使用组件的页面的json文件中添加
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
}
使用自定义组件
在要使用组件的页面的json文件中添加
<Tabs></Tabs>
案例:头部导航栏
在组件的 wxml ⽂件中编写组件模板,在 wxss ⽂件中加⼊组件样式
编辑wxml
<!-- 不通过data数据方式 -->
<view class="tabs">
<view class="tabs_title">
<view class="title_item active">首页</view>
<view class="title_item">原创</view>
<view class="title_item">分类</view>
<view class="title_item">关于</view>
</view>
<view class="tabs_content">内容</view>
</view>
<!--
1. 通过js 循环输出数据
2. {{item.isActive?'active':''}} 三元运算符 如果是isActive 就加上 class样式 active
3. 通过 item. 的方式来获取 data 里面的数据
-->
<view class="tabs">
<view class="tabs_title">
<view
wx:for="{{tabs}}"
wx:key="id"
class="title_item {{item.isActive?'active':''}}"
>
{{item.name}}
</view>
</view>
<view class="tabs_content">内容</view>
</view>
编辑wxss样式
.tabs{}
.tabs_title{
display: flex;
padding:10 rpx 0;
}
.title_item{
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.active{
color: red;
border-bottom: 5rpx solid currentColor;
}
.tabs_content{}
编辑 js 数据
data: {
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
}
效果图
导航栏绑定点击事件(标题激活选中功能)
在组件wxml 中,添加
bindtap=“hanldeItemTap” 事件绑定和
data-index="{{index}}" 数据传参
<view class="tabs">
<view class="tabs_title">
<view
wx:for="{{tabs}}"
wx:key="id"
class="title_item {{item.isActive?'active':''}}"
bindtap="hanldeItemTap"
data-index="{{index}}"
>
{{item.name}}
</view>
</view>
<view class="tabs_content">内容</view>
</view>
在组件js的methods中添加事件回调函数
- 页面的js中 的事件回调函数, 存放在 data 同层级下
- 但是在组件js中 的 事件回调函数 , 存放在 methods 中!!!
过程:
1. 绑定点击事件, 需要在methods 中绑定
2. 点击事件触发的时候, 获取被点击的索引, 就是事件传参 在wxml中通过 data-index="{{index}}" 方式传参
3. 获取原数组
4. 对数组循环
1. 给每一个循环项 的 选中属性 改为 false
2. 给当前的索引的项 添加 激活选中效果
methods: {
hanldeItemTap(e){ //绑定点击事件
//获取索引
const {index} = e.currentTarget.dataset;
console.log(index);
//获取data中的数组,
//如果使用页面传输过来的数据 , 那么获取的就不是data中的数据, 而是properties中的页面专递过来的数据
let {tabs} = this.data;
//循环数组
//数组.foreach(v,i) 可以用来遍历数组, 修改了v, 也会导致原数组 被修改
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
console.log(tabs);
this.setData({
tabs
})
}
}
父(页面组件)向子(自定义组件)传递数据
前面将导航标题的data数据定义在自定义组件中,这样不能适用于多个页面,
所以需要将导航data数据放在页面js中,
通过属性方式传递数据
- 父组件(页面) 向 子组件(自定义组件) 传递数据 通过标签属性的方式来传递
- 在子组件上进行接收
- 把这个数据当成是data中的数据直接用即可
1.先找到页面的wxml文件,为标签添加属性aaa
<Tabs tabs="{{tabs}}"></Tabs>
2.在自定义组件的js文件里的properties属性里进行接收
properties里面的接收的数据可以像在data中一样使用
Component({
/**
* 里面存放的是要从父组件接收的数据
*/
properties: {
//要接受的数据的名称
tabs:{
//type : 要接受的数据的类型
type:String,
// value 默认值
value:""
}
},
data{
}
3.剪切自定义组件中的data中的数据,拷贝到页面的js的data属性中
data: {
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
}
子(自定义组件)向父(页面组件)传递数据
点击 事件触发的时候,
触发父组件(页面)中的自定义事件, 同时传递数据给 父组件
- 子向父传递数据, 通过事件的方式传递
- 在子组件的标签上加入一个自定义事件
在父组件的xml文件中添加
<!-- 在自定义组件中触发了itemChange 事件,
相当于触发了binditemChange事件,相当于执行了handleItemChange方法 -->
<Tabs tabs="{{tabs}}" binditemChange= "handleItemChange"></Tabs>
在自定义组件中的js文件的hanldeItemTap点击事件中添加
this.triggerEvent(“itemChange”,{index})
将对数组的操作放在页面js中
注释掉自定义组件的数组操作
1. 绑定点击事件, 需要在methods 中绑定
2. 点击事件触发的时候, 获取被点击的索引, 就是事件传参 在wxml中通过 data-index="{{index}}" 方式传参
3. 获取原数组
4. 对数组循环
1. 给每一个循环项 的 选中属性 改为 false
2. 给当前的索引的项 添加 激活选中效果
5. 点击 事件触发的时候,
1. 触发父组件(页面)中的自定义事件, 同时传递数据给 父组件
2. 通过triggerEvent触发事件,将自定义组件的数据返回给页面组件
this.triggerEvent("父组件自定义事件的名称",{要传递的参数})
hanldeItemTap(e){ //绑定点击事件
//获取索引
const {index} = e.currentTarget.dataset;
this.triggerEvent("itemChange",{index})
//获取data中的数组, 如果使用页面传输过来的数据 , 那么获取的就不是data中的数据, 而是properties中的页面专递过来的数据
// let {tabs} = this.data;
//循环数组
//数组.foreach(v,i) 可以用来遍历数组, 修改了v, 也会导致原数组 被修改
// tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
//如果是从页面传递过来的数据,那么data属性中是没有数据的
//这里setData ,相当于在data中加了tabs数组
// this.setData({
// tabs
// })
}
}
在页面js中编写自定义事件,
//自定义事件, 用来接收子组件传递的数据的
handleItemChange(e){
//接收传递过来的参数, 拿到要操作的索引
const {index} = e.detail;
//拿到原数组
let {tabs} = this.data;
//循环数组, 根据点击的索引修改数组
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
//将修改后的值填充回data
this.setData({
tabs
})
}
传参的时候,在自定义组件中通过 this.triggerEvent(“itemChange”,{index}) 事件
将index参数, 传递给页面组件的 binditemChange 事件 ,
binditemChange 事件调用页面js中的 handleItemChange 方法,
参数index也随着传递给 handleItemChange 回调函数, 通过打印 e , 可以在 e 里面的detail中找到携带的index参数
效果图:
当点击原创后 , data中 的 tabs 数组也随着改变
点击导航实现改变内容 : slot标签
1.在自定义组件标签中添加 slot标签
- slot标签 只是一个占位符没有实际意义 , 一个插槽
- 当页面组件调用自定义组件的时候, 传递标签过来 , 传递过来的标签会替换掉 slot标签
<view class="tabs">
<view class="tabs_title">
<view
wx:for="{{tabs}}"
wx:key="id"
class="title_item {{item.isActive?'active':''}}"
bindtap="hanldeItemTap"
data-index="{{index}}"
>
{{item.name}}
</view>
</view>
<view class="tabs_content">
<slot></slot>
</view>
</view>
2.在页面组件的wxml文件中添加标签
【小程序】block标签的介绍和使用
并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
通过wx:if判断 当前是data中哪个是isActive
<Tabs tabs="{{tabs}}" binditemChange= "handleItemChange">
<block wx:if="{{tabs[0].isActive}}">0</block>
<block wx:elif="{{tabs[1].isActive}}">1</block>
<block wx:elif="{{tabs[2].isActive}}">2</block>
<block wx:elif="{{tabs[3].isActive}}">3</block>
</Tabs>
当点击导航栏的时候 , 下面的内容也会发生改变
最后附上完整代码:
页面wxml:
<!--
1. 父组件(页面) 向 子组件(自定义组件) 传递数据 通过标签属性的方式来传递
1. 在子组件上进行接收
2. 把这个数据当成是data中的数据直接用即可
2. 子向父传递数据, 通过事件的方式传递
1. 在子组件的标签上加入一个自定义事件
-->
<!-- 在自定义组件中触发了itemChange 事件,
相当于触发了binditemChange事件,相当于执行了handleItemChange方法 -->
<Tabs tabs="{{tabs}}" binditemChange= "handleItemChange">
<block wx:if="{{tabs[0].isActive}}">0</block>
<block wx:elif="{{tabs[1].isActive}}">1</block>
<block wx:elif="{{tabs[2].isActive}}">2</block>
<block wx:elif="{{tabs[3].isActive}}">3</block>
</Tabs>
页面js:
// pages/demo11/demo11.js
Page({
data: {
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
},
//自定义事件, 用来接收子组件传递的数据的
handleItemChange(e){
//接收传递过来的参数, 拿到要操作的索引
console.log(e);
const {index} = e.detail;
//拿到原数组
let {tabs} = this.data;
//循环数组, 根据点击的索引修改数组
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
//将修改后的值填充回data
this.setData({
tabs
})
}
})
自定义组件wxml:
<!-- 不通过data数据方式 -->
<!-- <view class="tabs">
<view class="tabs_title">
<view class="title_item active">首页</view>
<view class="title_item">原创</view>
<view class="title_item">分类</view>
<view class="title_item">关于</view>
</view>
<view class="tabs_content">内容</view>
</view> -->
<!--
1. 通过js 循环输出数据
2. {{item.isActive?'active':''}} 三元运算符 如果是isActive 就加上 class样式 active
3. 通过 item. 的方式来获取 data 里面的数据
-->
<view class="tabs">
<view class="tabs_title">
<view
wx:for="{{tabs}}"
wx:key="id"
class="title_item {{item.isActive?'active':''}}"
bindtap="hanldeItemTap"
data-index="{{index}}"
>
{{item.name}}
</view>
</view>
<view class="tabs_content">
<slot></slot>
</view>
</view>
自定义组件wxss:
.tabs_title{
display: flex;
padding:10 rpx 0;
}
.title_item{
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.active{
color: red;
border-bottom: 5rpx solid currentColor;
}
自定义组件js:
// components/Tabs/Tabs.js
Component({
/**
* 里面存放的是要从父组件接收的数据
*/
properties: {
//要接受的数据的名称
tabs:{
//type : 要接受的数据的类型
type:Array,
// value 默认值
value:[]
}
},
data: {
},
/*
1. 页面的js中 的事件回调函数, 存放在 data 同层级下
2. 但是在组件js中 的 事件回调函数 , 存放在 methods 中!!!!!!
*/
methods: {
/*
1. 绑定点击事件, 需要在methods 中绑定
2. 点击事件触发的时候, 获取被点击的索引, 就是事件传参 在wxml中通过 data-index="{{index}}" 方式传参
3. 获取原数组
4. 对数组循环
1. 给每一个循环项 的 选中属性 改为 false
2. 给当前的索引的项 添加 激活选中效果
5. 点击 事件触发的时候,
1. 触发父组件(页面)中的自定义事件, 同时传递数据给 父组件
2. this.triggerEvent("父组件自定义事件的名称",{要传递的参数})
*/
hanldeItemTap(e){ //绑定点击事件
//获取索引
const {index} = e.currentTarget.dataset;
this.triggerEvent("itemChange",{index})
//获取data中的数组, 如果使用页面传输过来的数据 , 那么获取的就不是data中的数据, 而是properties中的页面专递过来的数据
// let {tabs} = this.data;
//循环数组
//数组.foreach(v,i) 可以用来遍历数组, 修改了v, 也会导致原数组 被修改
// tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
//如果是从页面传递过来的数据,那么data属性中是没有数据的
//这里setData ,相当于在data中加了tabs数组
// this.setData({
// tabs
// })
}
}
})
其他属性
properties
组件的对外属性,是属性名到属性设置的映射表,参⻅下⽂
data
组件的内部数据,和 properties ⼀同⽤于组件的模板渲染
observers
组件数据字段监听器,⽤于监听properties和data的变化,参⻅数据监听器
methods
组件的⽅法,包括事件响应函数和任意的⾃定义⽅法,关于
事件响应函数的使⽤,参⻅组件间通信与事件
created
组件⽣命周期函数,在组件实例刚刚被创建时执⾏,注意此
时不能调⽤ setData ,参⻅组件⽣命周期
attached
组件⽣命周期函数,在组件实例进⼊⻚⾯节点树时执⾏,参⻅组件⽣命周期
ready
组件⽣命周期函数,在组件布局完成后执⾏,参⻅组件⽣命周期
moved
组件⽣命周期函数,在组件实例被移动到节点树另⼀个位置时执⾏,参⻅组件⽣命周期
detached
组件⽣命周期函数,在组件实例被从⻚⾯节点树移除时执
⾏,参⻅组件⽣命周期
总结
- 右击,新建Component , 创建自定义组件
- 在页面json的usingComponents属性中配置自定义组件路径
- 在页面wxml中直接使用组件名称标签
- 组件的事件回调函数存放在methods下,而不是和data同级
- ⽗组件通过属性的⽅式给⼦组件传递参数
- ⼦组件通过事件的⽅式向⽗组件传递参数