# 组件结构

# wxml 文件

对于一个自定义组件,我们考虑的是其模板性,也就是不能够将代码写死。因此我们需要在 view 标签里通过调用 js 文件的 data 数据(初始化数据)来进行数据的访问。wx:for="",wx:key="数组中的某个唯一属性"。同时我们需要在 bindtap 属性定义事件绑定函数

wxml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<view class="tabs">
<!-- <view class="tabs_title">
<view class="title_item">首页</view>
<view class="title_item">原创</view>
<view class="title_item">分类</view>
<view class="title_item">关于</view>
</view> -->
<view class="tabs_title">
<view wx:for="{{tabs}}" wx:key="id" class="title_item {{item.isActive?'active':''}}" bindtap="handleItemTap" data-index="{{index}}">
{{item.name}}
</view>
</view>
<view class="tabs_content">
<!-- slot标签就是一个占位符 插槽
等到父组件调用子组件的时候再传递标签过来,
最终这些被传递的标签就会替换slot插槽的位置 -->
<slot></slot>
</view>
</view>

# wxss 文件

定义了一些类的样式,其中 flex 布局需要重点关注,这对于弹性布局非常重要。

wxss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.tabs{}
.tabs_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 currentColor;
}
.tabs_content{}

# js 文件

我们需要在 data:{} 中定义我们需要预先定义的数据,然后在 methods:{} 中定义事件的绑定函数,这与页面 js 文件的位置不同(见代码和注释)。然后在 view 标签中定义一个被点击的自定义索引:data-index=""

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Component({
/**
* 组件的属性列表
*/
// 存放的是从父组件中接受的数据
properties: {
// 要接收的数据名称
tabs: {
// type 要接收的数据类型
type: Array,
// value 默认值
value: []
}
},

/**
* 组件的初始数据
*/
data: {

},

/**
* 组件的方法列表
*/
/*
1. 页面.js文件中 存放事件回调函数的时候存放在data同层级下
2. 组件.js文件中 存放事件回调函数的时候 必须要存放在methods中
*/
methods: {
handleItemTap(e) {
// 1 绑定点击事件 需要在methods中绑定
// 2 获取被点击的索引
// 3 获取原数组
/* 4 对数组循环
1 给每一个循环项选中属性改为false
2 给当前索引项添加激活选中效果就可以了
5 点击事件触发的时候,需要触发父组件中的自定义事件,同时传递数据给父组件
this.triggerEvent("父组件自定义事件的名称", 要传递的参数)
*/

// 2 获取索引
const { index } = e.currentTarget.dataset;
// 5 触发父组件中的自定义事件,同时传递参数给父组件
this.triggerEvent("itemChange", { index });
}
}
})

# json 文件

默认即可

json
1
2
3
4
{
"component": true,
"usingComponents": {}
}

# 页面(组件的父组件)结构

# wxml 文件

wxml
1
2
3
4
5
6
7
8
9
<!-- 1. 父组件(页面)向子组件传递数据通过标签属性的方式来传递 
1.1 在子组件上进行接收
2. 子向父传递数据,通过事件的方式传递 -->
<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 文件

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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) {
// 接收传递过来的参数
const { index } = e.detail;
// 解构:对复杂类型进行解构时,复制了一份变量的引用而已
// let tabs = this.data.tabs
// 最严谨的做法是重新拷贝一份数组,再对数组的备份进行处理
// let tabs=JSON.parse{JSON.stringify(this.data.tabs)};
let { tabs } = this.data;
// [].forEach 遍历数组 遍历数组时修改了v,也会导致原数组被修改
tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
// 会将tabs传给自己的data中
this.setData({
tabs
})
}
// ......
})

# json 文件

我们需要在 usingComponents 中找到我们自定义组件的相对路径。注意,由于组件包含了 wxml,wxss,js,json 四个同名文件,因此我们在最后的 Tabs 后不需要添加后缀名。

json
1
2
3
4
5
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
}
}

# 过程分析

  1. 首先我们在 Tabs 组件 wxml 文件中定义一个模板,其中包含了 view 标签和所属的 class 类,用于对应组件 wxss 文件中定义的布局
  2. 组件的事件回调函数需要定义在 methods 中,在组件 wxml 文件标签中,有这样两个属性:
    1. bindtap:用于指定点击事件的触发函数名 (handleItemTap)
    2. data-index:用于指定数据的索引
  3. 进入组件的 js 文件中,在 methods 中绑定点击事件 handleItemTap (){},在该事件函数中我们首先获取索引,然后触发父组件的自定义事件,同时传递参数给父组件 (this.triggerEvent ("itemChange", { index }))。这里并没有直接对原数组进行修改,因为在组件中对父页面的 data 数据进行修改,是无效的
  4. 在页面 wxml 文件的 Tabs 自定义组件标签中绑定单击响应函数 (binditemChange="handleItemChange"),然后在页面 js 文件中定义这个函数(与 data 平行,这与组件中函数位置有所不同)。

# 需要注意的地方

  1. 组件接收父组件的数据位置在 properties 中,需要指定数据名,数据类型和默认 value
  2. 我们没有将主要的处理步骤放在组件的事件回调函数中,是因为 setData 的时候又将修改过的 tabs 存进了自己的 data 区,也就是说,在 properties 有一个 tabs 数组,在自己的 data 区又有一个 tabs 数组,这会出问题
  3. 组件 wxml 文件中 slot 标签的作用:一个占位符 插槽,等到父组件调用子组件的时候再传递标签过来,最终这些被传递的标签就会替换 slot 插槽的位置,也就是我们样例中页面 wxml 文件的 wx:if 语句选择的那个标签