类似于页面,一个自定义组件由json,wxml,wxss,js四个文件组成。
创建自定义组件的文件夹
先新建一个components文件夹(和pages文件夹同层级),再在文件夹中创建一个文件夹,里面新建与第二层文件夹同名的文件,并更改文件名后缀为.wxss或者其他三个,就会自动生成一个自定义的组件(或者回到微信开发者工具中右击第二层的文件夹,再去点击新增component)。而且还应该在组件的json文件中进行自定义组件声明。
{
"component": true,
"usingComponents": {}
}
想在页面中使用自定义组件的时候,就在页面配置.json文件中添加引用代码
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
}
}
这样就可以把自定义的组件当作普通标签来使用了。
一.简单的使用自定义组件来控制页面显示
下面根据一个 需求:像淘宝那样点击上方的标题栏就可以切换标题栏显示。来演示自定义组件
首先是标题栏对象的名称数据的设定与标题点击事件的处理,在js文件中编码
// components/Tabs/Tabs.js
Component({
/**
* 组件的初始数据
*/
data: {
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
},
/**
* 组件的方法列表
*/
/*
1.页面.js文件中,存放事件回调函数的时候,存放在data同层级下
2.组件.js文件中,存放事件回调函数的时候,必须要存在methods中
*/
methods:
{
handleItemtap(e){
/*
1 绑定点击事件 需要在methods中绑定
2 获取被点击的索引
3 获取原数组
4 对数组循环
(1)给每一个循环项的选中属性改为false
(2)给当前的索引的项添加激活选中效果就可以了*/
//获取索引
const {index}=e.currentTarget.dataset;
//获取data中的数组
//解构 对复杂类型进行解构的时候 复制了一份变量的引用而已
//最严谨的做法就是重新拷贝一份数组,再对这个数组的备份进行处理
//像 let tabs=this.data 这种也可以
//不要直接修改 this.data中的数据
//深拷贝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);
this.setData
({
tabs
})
}
}
})
其次是使用标签来实现标题栏并绑定点击事件
这里的处理很值得思考,每一个view小标题绑定一个点击事件,每一次点击标题就把标题数据的判断设为true,再通过三元表达式来设定样式active.
<view class="tabs">
<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是 view所获样式的名称,可以通过在css文件中进行样式的设计再在标签中进行样式的选择
.tabs{}
.tabs_title{
display: flex;
padding: 10rpx;
}
.title_item{
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.active{
color:red;
border-bottom: 5px solid currentColor;
}
但是这样仅仅实现了 在点击标题栏的时候,对应的标题栏可以有相应的显示,但是页面显示不变,意味着并没有切换页面。
二.父组件(页面)向子(自定义组件)传递数据
父组件向子组件传递数据通过标签属性的方式传递
1)在子组件上接收
2)把这个数据当成data中的数据使用即可
3)子组件接收父组件的数据应当放在自定义组件的js文件中的Component中的properties字段,该字段中存放的是从父组件中 接收的数据
父组件传递一个字符串
<Tabs message="520">
</Tabs>
子组件接收数据
Component({
/**
* 组件的属性列表
*/
properties: {//里面存放的是要从父组件中接收的数据
//要接受的数据的名称
message:{
//type 要接收的数据的类型
type:String,
//value 默认值
value:""
}
}
})
子组件使用数据
<view>
{{message}}
</view>
父组件传递一个对象数组
对象数组的数据存储在data中,由于在父组件中数据已经定义存储过了,那么在子组件中就不能重复定义
Page({
/**
* 页面的初始数据
*/
data: {
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
}
})
通过父组件传递——谨记,变量一定要打上 {{}} 来使用!!!!
<Tabs tabs="{{tabs}}" >
</Tabs>
子组件接收数据
Component({
/**
* 组件的属性列表
*/
properties: {//里面存放的是要从父组件中接收的数据
//要接受的数据的名称
tabs:{
type:Array,
value:""
}
}
})
子组件使用数据:和在子组件中定义数据差不多,都可以直接使用。
<view class="tabs">
<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>
如果是从父组件中传递数据过来依然使用下列的函数接口方法,那么可能会出现一定的问题,至于什么问题我没看懂!!!但是解决这类问题的方法就是从子组件向父组件传递数据,去修改父组件中对象数组中的isActive值(个人理解可能下面的方法修改的是子组件中的备份数据,而原数据没有被修改)。
// components/Tabs/Tabs.js
Component({
/**
* 组件的属性列表
*/
properties: {//里面存放的是要从父组件中接收的数据
tabs:{
type:Array,
value:""
}
},
/**
* 组件的方法列表
*/
/*
1.页面.js文件中,存放事件回调函数的时候,存放在data同层级下
2.组件.js文件中,存放事件回调函数的时候,必须要存在methods中
*/
methods:
{
handleItemtap(e){
/*
1 绑定点击事件 需要在methods中绑定
2 获取被点击的索引
3 获取原数组
4 对数组循环
(1)给每一个循环项的选中属性改为false
(2)给当前的索引的项添加激活选中效果就可以了*/
//获取索引
const {index}=e.currentTarget.dataset;
//获取data中的数组
//解构 对复杂类型进行解构的时候 复制了一份变量的引用而已
//最严谨的做法就是重新拷贝一份数组,再对这个数组的备份进行处理
//像 let tabs=this.data 这种也可以
//不要直接修改 this.data中的数据
//深拷贝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);
this.setData
({
tabs
})
}
}
})
三.子组件向父组件传递数据
点击事件触发的时候,需要触发父组件的自定义事件,同时传递数据给父组件。
格式:this.triggerEvent("父组件自定义事件的名称",要传递的参数)
子向父传递数据,通过事件的方式传递,在子组件的标签上加入一个自定义事件
子组件事件触发的同时触发父组件事件
// components/Tabs/Tabs.js
Component({
/**
* 组件的属性列表
*/
properties: {//里面存放的是要从父组件中接收的数据
//要接受的数据的名称
message:{
//type 要接收的数据的类型
type:String,
//value 默认值
value:""
},
tabs:{
type:Array,
value:""
}
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
/*
1.页面.js文件中,存放事件回调函数的时候,存放在data同层级下
2.组件.js文件中,存放事件回调函数的时候,必须要存在methods中
*/
methods:
{
handleItemtap(e){
/*点击事件触发的时候
触发父组件中的自定义事件,同时传递数据给父组件
this.triggerEvent("父组件自定义事件的名称",要传递的参数)
*/
//获取索引
const {index}=e.currentTarget.dataset;
//触发父组件中的自定义事件
this.triggerEvent("itemChange",{index});
}
}
})
绑定标签和子组件的自定义事件
<view class="tabs">
<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>
父组件中的数据与自定义事件
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.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);//修改原数组
//把值填充回去
this.setData
({
tabs
})
}
})
父组件自定义组件标签绑定自定义事件
<Tabs message="520" tabs="{{tabs}}" binditemChange="handleItemchange">
</Tabs>
这样上面所提出的问题就有所解决了!!
四. slot组件
上面所写的代码无论怎么切换点击的标题栏,所显示的页面总是一样的,下面我们可以使用slot组件来实现不同的标题栏拥有不同的显示。
<slot>标签其实就是一个占位符插槽,等到父组件调用子组件的时候再传递标签过来最终这些被传递的标签就会替换slot插槽的位置,传递标签的方法就是直接在父组件中的自定义标签中写下标签与内容。
我们根据数组元素的isActive来判断点击标题与对应的父向子传递的标签与内容
<Tabs message="520" tabs="{{tabs}}" binditemChange="handleItemchange">
<block wx:if="{{tabs[0].isActive}}">被风吹过的夏天</block>
<block wx:elif="{{tabs[1].isActive}}">爱的鼓励</block>
<block wx:elif="{{tabs[2].isActive}}">你是对的人</block>
<block wx:else="{{tabs[3].isActive}}">多远都要在一起</block>
</Tabs>
子组件接收并显示内容
<view>
{{message}}
</view>
<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
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>
其他文件就是父子相互传递数据的文件了