哈喽小伙伴们,上一期为大家讲解了一下小程序的代码构成并且搭建了一个简单的钢琴小程序,不知道大家有没有扫码去体验一下呢;这期给大家说一说小程序的自定义组件,并且搭建一个自定义的底部Tabbar栏;话不多说,咱们直接开整!
写Vue的小伙伴们应该都知道,Vue有一个很重要的思想就是组件化
,为的是一次封装,多次复用
;带来的好处就是当我们的业务功能重复繁琐时候就可以把主要功能抽离出来封装成组件,便于我们在多个地方去使用;还有就是框架的组件并不符合我们的实际业务功能,那么这个时候也需要我们去独立封装一个适用的组件。在我们微信小程序里边,也有组件化的思想,那它是怎么定义的呢?
小程序自定义组件
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
从小程序基础库版本
1.6.3
开始,小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本1.6.3
或更高。
类似于页面,一个自定义组件由
json、
wxml、
wxss、
js
4个文件组成。要编写一个自定义组件,首先需要在 json 文件中进行自定义组件声明(将component
字段设为true
可将这一组文件设为自定义组件):
{
"component": true
}
同时,还要在
wxml
文件中编写组件模板,在wxss
文件中加入组件样式,它们的写法与页面的写法类似。
代码示例:
<view class="inner">
{{innerText}}
view>
<slot>slot>
/* 这里的样式只应用于这个自定义组件 */
.inner {
color: red;
}
注意:在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。
在自定义组件的 js 文件中,需要使用
Component()
来注册组件,并提供组件的属性定义、内部数据和自定义方法。 组件的属性值和内部数据将被用于组件wxml
的渲染,其中,属性值是可由组件外部传入的。
代码示例:
Component({
properties: {
// 这里定义了innerText属性,属性值可以在组件使用时指定
innerText: {
type: String,
value: 'default value',
}
},
data: {
// 这里是一些组件内部数据
someData: {}
},
methods: {
// 这里是一个自定义方法
customMethod: function(){}
}
})
使用已注册的自定义组件前,首先要在页面的
json
文件中进行引用声明。此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径:
{
"usingComponents": {
"component-tag-name": "path/to/the/custom/component"
}
}
这样,在页面的 wxml 中就可以像使用基础组件一样使用自定义组件。节点名即自定义组件的标签名,节点属性即传递给组件的属性值。
开发者工具 1.02.1810190 及以上版本支持在 app.json
中声明 usingComponents
字段,在此处声明的自定义组件视为全局自定义组件,在小程序内的页面或自定义组件中可以直接使用而无需再声明。
代码示例:
在开发者工具中预览效果
<view>
<!-- 以下是对一个自定义组件的引用 -->
<component-tag-name inner-text="Some text"></component-tag-name>
</view>
自定义组件的 wxml 节点结构在与数据结合之后,将被插入到引用位置内。
一些需要注意的细节:
注意,是否在页面文件中使用 usingComponents 会使得页面的 this 对象的原型稍有差异,包括:
如果页面比较复杂,新增或删除 usingComponents 定义段时建议重新测试一下。
根目录下创建
component
文件夹,在其下创建tabbar
文件夹,然后再tabbar文件夹里创建名为index
的Component
component 设置为 true,即为自定义组件
{
"component": true
}
<view class="tabbar" style="width: 500rpx;height: 300rpx;">
<view class="item {{index==idx?'active':''}}" wx:for="{{tabBar}}" wx:for-index="idx" bindtap="goto" data-index='{{idx}}' data-path="{{item.path}}">
<i class="iconfont {{item.icon}} icon">i>
<view class="text">{{item.name}}view>
view>
<view class="move" style="left:{{115+(index*210)}}rpx">view>
view>
.tabbar{
width: 100% !important;
height: 140rpx !important;
position: fixed;
bottom: 0;
left: 0;
background: #181c27;
display: flex;
justify-content: center;
box-sizing: border-box;
padding:0 60rpx;
}
.tabbar .item{
flex: 1;
color: #fff;
height: 100rpx;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
position: relative;
z-index: 10;
}
.tabbar .item .text{
position: absolute;
width: 100%;
bottom: 10rpx;
text-align: center;
font-size: 22rpx;
opacity: 0;
transition: all .8s;
transform: scale(0.8);
width: 100%;
}
.tabbar .item.active .text{
opacity: 1;
transform: scale(1);
}
.tabbar .item.active .icon{
color: #3561f5;
transform: translateY(-55rpx);
}
.tabbar .item .icon{
font-size: 50rpx!important;
text-align: center;
transition: all .8s;
}
page{
width: 100%;
height: 100%;
/* background: #404655; */
}
.move{
width: 100rpx;
height: 100rpx;
background: #ffffff;
box-shadow: 0rpx 0rpx 50rpx 2rpx rgba(255, 255, 255, 0.9);
border-radius: 50%;
position: absolute;
left: 115rpx;
top: -35%;
transition: all .3s;
z-index: 1;
}
Component({
options: {
addGlobalClass: true
},
behaviors: [],
properties: { // 类似于Vue的props,用来接收父组件传递的参数
myProperty: { // 属性名
type: String,
value: ''
},
myProperty2: String // 简化的定义方式
},
data: {
index: 0, // 默认显示第一个(首页)
// tabbar栏切换,这三个页面要提前建好,放在根目录下的pages里
tabBar: [{
name: '首页',
icon: 'icon-home',
path: '../../pages/home/index'
},
{
name: '商品',
icon: 'icon-all',
path: '../../pages/shop/shop'
},
{
name: '我的',
icon: 'icon-bussiness-man',
path: '../../pages/my/my'
},
]
}, // 私有数据,可用于模板渲染
lifetimes: {
// 生命周期函数,可以为函数,或一个在methods段中定义的方法名
attached: function () {},
moved: function () {},
detached: function () {},
},
// 生命周期函数,可以为函数,或一个在methods段中定义的方法名
attached: function () {}, // 此处attached的声明会被lifetimes字段中的声明覆盖
ready: function () {},
pageLifetimes: {
// 组件所在页面的生命周期函数
show: function () {},
hide: function () {},
resize: function () {},
},
methods: {
goto(e) {
// console.log(e.currentTarget.dataset.index);
if (e.currentTarget.dataset.index != this.data.index) {
this.setData({
index: e.currentTarget.dataset.index
})
}
// this.triggerEvent 类似于Vue的 this.$emit; 用来子组件给父组件传递参数
// 这里我们把每次点击的Tab栏下标传递给父组件
this.triggerEvent("itemChange",{index:e.currentTarget.dataset.index});
},
onMyButtonTap: function () {
this.setData({
// 更新属性和数据的方法与更新页面数据的方法类似
})
},
// 内部方法建议以下划线开头
_myPrivateMethod: function () {
// 这里将 data.A[0].B 设为 'myPrivateData'
this.setData({
'A[0].B': 'myPrivateData'
})
},
_propertyChange: function (newVal, oldVal) {
}
}
})
找到pages/home
文件夹:
{
"usingComponents": {
"tab-bar":"../../component/tabbar1/index",
"my":"../../pages/my/my",
"shop":"../../pages/shop/shop"
},
"navigationBarTitleText": "首页"
}
把我们封装好的
tab-bar
组件注册进来,属性名随便写,类似Vue的组件名
;值为该组件的路径。
把Tabbar其余页面组件
也注册进来,后面会用到(页面跳转切换
)。
<view>
<block wx:if="{{currentIndex === 0}}">
<van-grid column-num="3" border="{{ false }}">
<van-grid-item use-slot wx:for="{{ 8 }}" wx:for-item="index">
<image style="width: 100%; height: 90px;" src="https://img.yzcdn.cn/vant/apple-{{ index + 1 }}.jpg" />
van-grid-item>
van-grid>
block>
<shop wx:if="{{currentIndex === 1}}">shop>
<my wx:if="{{currentIndex === 2}}">my>
<tab-bar bind:itemChange="handleItemChange">tab-bar>
view>
// index.js
// 获取应用实例
const app = getApp()
Page({
data: {
currentIndex: 0, // 初始下标,默认显示首页
},
handleItemChange(e) {
this.setData({
currentIndex: e.detail.index
})
if (this.data.currentIndex === 0) {
wx.setNavigationBarTitle({
title: '首页' //页面切换,更换页面标题
})
} else if (this.data.currentIndex === 1) {
wx.setNavigationBarTitle({
title: '商店' //页面切换,更换页面标题
})
} else(
wx.setNavigationBarTitle({
title: '我的' //页面切换,更换页面标题
})
)
},
onLoad() {},
})
这篇为小伙伴们讲解了在小程序当中,我们如何去自定义
或者封装我们自己的组件
;大家自己也要尝试一下,思想和Vue差不太多,相信小伙伴们一定也可以的,加油;各位小伙伴让我们 let’s be prepared at all times!
✨原创不易,还希望各位大佬支持一下!
点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!