类似vue或者react中的自定义组件
- ⼩程序允许我们使⽤⾃定义组件的⽅式来构建⻚⾯。
1. 创建⾃定义组件
类似于页面,一个自定义组件由
wxss
json
wxml
js
4个文件组成
- 可以在微信开发者⼯具中快速创建组件的⽂件结构,首先创建一个
component
文件夹用于存放自定义组件。
2. 声明组件
- ⾸先需要在组件的
json
⽂件中进⾏⾃定义组件声明;
{
"component": true,
"usingComponents": { // 可以使用其他组件}
}
-
新建一个页面使用自定义组件 ;
在新建的页面的
json
中使用自定义组件 :
{
"usingComponents": {
"Tabs": "../../component/Tabs/Tabs"
}
}
- 在
wxml
文件中使用引入的自定义组件:
<Tabs>
</Tabs>
3. 自定义组件-Tabs
样式优化
-
wxml
代码:
<!--
1. 自定义组件 - 优化组件样式
-->
<view class="tabs">
<view class="title">
<view wx:for="{{tabs}}" wx:key="{{item.id}}" class="title-item {{item.isActive ? 'active':''}}">
{{item.name}}
</view>
</view>
<view class="content">内容</view>
</view>
- 需要将
tabs
菜单的数据进行抽取到数组中进行遍历展示:
// component/Tabs/Tabs.js
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
tabs: [{
id: 0,
name: '首页',
isActive: true
},
{
id: 1,
name: '原创',
isActive: false
},
{
id: 2,
name: '分类',
isActive: false
},
{
id: 3,
name: '关于',
isActive: false
},
]
},
/**
* 组件的方法列表
*/
methods: {
}
})
- 编写样式文件 :
.title {
display: flex;
padding: 10rpx 0;
}
.title-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.active {
color: red;
border-bottom: 5rpx solid #f00;
}
4. 实现tabs
激活选中
- 需要为
view
绑定一个单击事件,并传递当前点击的item
的索引 。
<!--
1. 自定义组件 - 优化组件样式
-->
<view class="tabs">
<view class="title">
<view
wx:for="{{tabs}}"
wx:key="{{item.id}}"
class="title-item {{item.isActive ? 'active':''}}"
bindtap="handleItemTap"
data-index="{{index}}">
{{item.name}}
</view>
</view>
<view class="content">内容</view>
</view>
- 处理单击事件:
/**
* 组件的方法列表
*/
methods: {
handleItemTap(e) {
// 进行解构赋值 下面这句相当于 this.index = e.currentTarget.dataset.index
const { index } = e.currentTarget.dataset;
// 解构是对复杂类型进行解构的时候复制一份变量引用而已
// 最严谨的做法就是重新拷贝一份数组再对这个数组的备份进行处理
// 不要直接this.data.属性
let { tabs } = this.data;
// 下面是最严谨的写法
// let tabs = JSON.parse(JSON.stringify(this.data.tabs));
tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
this.setData({
tabs
})
}
}
- 注意单击事件中的解构赋值:
// 进行解构赋值 下面这句相当于 this.index = e.currentTarget.dataset.index
const { index } = e.currentTarget.dataset;
// 解构是对复杂类型进行解构的时候复制一份变量引用而已
// 最严谨的做法就是重新拷贝一份数组再对这个数组的备份进行处理
// 不要直接this.data.属性
let { tabs } = this.data;
// 下面是最严谨的写法
// let tabs = JSON.parse(JSON.stringify(this.data.tabs));
5. 父组件向子组件传递数据
- 在父组件引用子组件的
page
中的标签上传递一个数据: 里面的属性名称任意,符合命名规范
<Tabs parent="父亲"></Tabs>
- 在子组件的
properties
中接收父组件传递的值:
此时便可以在子组件的标签中直接使用该属性。
将上面的
tabs
数组通过父传子的方式,从父组件将数据传递到子组件:
- 但是以上的写法是有问题的 : 因为子组件中修改了
tabs
的数据,但是父组件中的数据并未实现修改:
- 实现父组件中数据的修改需要使用到子组件向父组件传递数据的方式。
6. 子组件向父组件传递数据
- 在父组件中定义一个父组件的自定义事件 :
bindItemChange
- 子组件向父组件通过事件的方式传递数据 :
methods: {
handleItemTap(e) {
// 进行解构赋值 下面这句相当于 this.index = e.currentTarget.dataset.index
const { index } = e.currentTarget.dataset;
// 点击tabs触发事件的时候需要传递数据给父组件 this.triggerEvent("父组件自定义事件的名称",要传递的参数)
// 子组件通过事件的方式向父组件传递数据 参数需要传递一个对象
this.triggerEvent("itemChange", { index });
}
}
- 父组件接收子组件通过事件传递的数据 :
handleItemChange(e) {
// 解构
const { index } = e.detail;
let { tabs } = this.data;
tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false)
this.setData({
// 这里 tabs 等价于 this.tabs = tabs
tabs
})
}
7. slot
插槽(占位符)标签
- 在自定义组件中使用
slot
标签进行占位 :
- 在父组件中定义需要显示的内容 :
8. 组件的其他属性
定义段 | 类型 | 是否必填 | 描述 |
---|---|---|---|
properties |
ObjectMap |
否 | 组件的对外属性,是属性名到属性设置的映射表,参⻅下⽂ |
data |
Object |
否 | 组件的内部数据,和 properties ⼀同⽤于组件的模板渲染 |
observers |
Object |
否 | 组件数据字段监听器,⽤于监听 properties 和data 的变化,参⻅数据监听器 |
methods |
Object |
否 | 组件的⽅法,包括事件响应函数和任意的⾃定义⽅法,关于事件响应函数的使⽤,参⻅组件事件 |
created |
Function |
否 | 组件⽣命周期函数,在组件实例刚刚被创建时执⾏,注意此时不能调⽤ setDatasetData ,参⻅组件⽣命周期 |
attached |
Function |
否 | 组件⽣命周期函数,在组件实例进⼊⻚⾯节点树时执⾏,参⻅组件⽣命周期 |
ready |
Function |
否 | 组件⽣命周期函数,在组件布局完成后执⾏,参⻅组件⽣命周期 |
moved |
Function |
否 | 组件⽣命周期函数,在组件实例被移动到节点树另⼀个位置时执⾏,参⻅组件⽣命周期 |
detached |
Function |
否 | 组件⽣命周期函数,在组件实例被从⻚⾯节点树移除时执⾏,参⻅组件⽣命周期 |