本文提供给急需使用自定义组件人群,以下是博主个人理解和案例!可以辅助官网来看
从小程序基础库版本 1.6.3 开始,小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
类似页面,一个自定义组件由json ,wxml,wxss,js 4个文件组成
1、在根目录下自定义一个components文件夹,用来存放自定义的组件。
2、再针对每一个组件创建一个文件夹,用来存放这个组件相关的文件。、
在这里插入图片描述
在需要引入组件的页面的json文件中,在usingComponents里面写键值对,写组件名和路径
{
"usingComponents": {
//引用组件
"cell": "/components/cell/cell",
"item": "/components/item/item"
}
}
在需要引入组件的页面的wxml文件中,添加组件标签
<item>使用item自定义组件</item>
效果:
生命周期:lifetimes
组件的挂载: attach
数据:data
方法:methods
属性(只读):properties
外部类:externalClasses
选项:options
多个插槽:multipleSlots:true
组件的样式格式: styleIsolation:“isolated”
组件对应 wxss 文件的样式,只对组件 wxml 内的节点生效。编写组件样式时,需要注意以下几点:
1,组件和引用组件的页面不能使用 id 选择器(#a)、属性选择器([a])和标签名选择器,请改用 class 选择器。
2,组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,如遇,请避免使用。
3,子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。
4,继承样式,如 font 、 color ,会从组件外继承到组件内。
5,除继承样式外, app.wxss 中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)。
除此以外,组件可以指定它所在节点的默认样式,使用 :host 选择器(需要包含基础库 1.7.2 或更高版本的开发者工具支持)。
组件:cell.js
// components/cell/cell.js
Component({
// 选项:
options:{
// 样式隔离:apply-shared 父影响子,shared父子相互影响, isolated相互隔离
styleIsolation:"isolated",
// 允许多个插槽
multipleSlots:true,
},
styleIsolation : " 值 ",
值:
apply-shared :父影响子
shared:父子相互影响
isolated:相互隔离
// 通过组件的外部类实现父组件控制子自己的样式
externalClasses:["cell-class"],
在组件:cell.js 中定义外部类
<view class="cell cell-class">我是cell组件</view>
使用组件
<cell cell-class="mycell"></cell>
在页面设组件样式
.mycell{
line-height: 120rpx !important;
color:#F70;
}
父组件
<cell>
<text>插槽内容</text>
</cell>
子组件
<view><slot></slot></view>
父组件com.wxml(页面)
<cell>
<text slot="pre"> 🚒</text>
<text slot="next">🥗</text>
</cell>
子组件 cell.js
// 允许多个插槽
options:{ multipleSlots:true}
子组件 cell.wxml
<view>
<slot name="pre"></slot>
<slot name="next"></slot>
</view>
允许多个插槽
options:{ multipleSlots:true}
父传子
property
子传参父
triggerEvent
组件 cell.js
/**
* 组件的方法列表
*/
methods: {
tapHd(){
this.setData({count:this.data.count+1})
// 发送一个事件
this.triggerEvent("cellclick",this.data.count)
}
}
组件 cell.wxml 使用插槽
<view class="cell" bindtap="tapHd">
<text>{{title}}</text>
<text>{{count}}</text>
</view>
此时还不能调用 setData
props
title:标题
icon :图标
tip :提示
url :跳转连接
open-type : 打开的方式
slot
right
left :插槽
event
click :事件
外部类itemClass :整体
titleClass :标题
item.js 代码:
// components/item/item.js
Component({
options:{
multipleSlots:true
},
externalClasses:["itemclass"],
/**
* 组件的属性列表
*/
properties: {
// 标题
title:{
type:String,
value:""
},
// 显示右侧插槽
showrslot:{
type:Boolean,
value:false,
},
// 图标
icon:{
type:String,
value:""
},
tip:{
type:String,
value:"",
},
badge:{
type:[Boolean,Number],
value:false
},
url:{
type:String,
value:""
},
openType:{
type:String,
value:"navigate"
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
itemclick(e){
console.log(e);
//发送一个事件
this.triggerEvent("itemclick",e.detail)
}
}
})
组件 item.json
{
"component": true,
"usingComponents": {}
}
组件 item.wxml
<navigator class="item itemclass" url="{{url}}" open-type="{{openType}}" bindtap="itemclick">
<view class="icon" wx:if="{{icon}}">
<image src="{{icon}}" mode="aspectFill"></image>
</view>
<view class="content">
<view class="title">
<view class="title" wx:if="{{title}}">{{title}}</view>
<slot name="title" wx:else ></slot>
</view>
<view class="right" wx:if="{{!showrslot}}">
<view class="tip">{{tip}}</view>
<view class="badge" wx:if="{{badge}}">
<view wx:if="{{badge===true}}" class="dot">
</view>
<view wx:else class="redbadeg">
{{badge}}
</view>
</view>
<view class="arrow"></view>
</view>
<slot name="right" wx:else></slot>
</view>
</navigator>
<!--
icon 图标
conent 内容
title 标题
right 右侧
tip 提示
badge 红点
arrow 箭头
-->
组件 item.wxss 代码:
/* components/item/item.wxss */
.item{
line-height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.icon{
margin-left: 30rpx;
margin-right: 30rpx;
height: 100%;
display: flex;
align-items: center;
}
.icon image{
width: 60rpx;
height: 60rpx;
}
.content{
padding: 0 30rpx;
border-bottom: 1rpx solid #ccc;
display: flex;
flex:1;
}
.title{
flex:1;
color:#333;
font-size: 32rpx;
}
.right{
display: flex;
align-items: center;
}
.right .arrow{
height: 30rpx;
width: 30rpx;
border-top:3rpx solid #999;
border-right: 3rpx solid #999;
transform: rotate(45deg);
}
.tip{
color:#999;
font-size: 24rpx;
}
.dot{
height: 14rpx;
width: 14rpx;
background-color: #f00;
border-radius: 12rpx;
margin-left: 15rpx;
}
.redbadeg{
font-size: 18rpx;
color:#fff;
border-radius: 18rpx;
background-color: #f00;
max-height: 30rpx;
width: 30rpx;
line-height: 30rpx;
text-align: center;
margin-left: 15rpx;
}
com.js 代码如下:
// pages/com/com.js
Page({
/**
* 页面的初始数据
*/
data: {
},
cellHd(e){
console.log(e);
wx.showToast({
title: '你点击了'+e.detail,
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
com.json 代码如下:
{
"usingComponents": {
"cell":"/components/cell/cell",
"item":"/components/item/item"
}
}
com.wxml 代码如下:
<item title="支付" icon="/images/icon01.png"></item>
<item title="相册" icon="/images/icon02.png"></item>
<item title="支付" ></item>
<item title="消息" icon="/images/icon02.png" badge="{{true}}" tip="3条未读"></item>
<item title="消息" icon="/images/icon02.png" badge="{{12}}" tip="12条未读"></item>
<item title="消息" icon="/images/icon02.png" showrslot="{{true}}">
<switch checked="true" slot="right"></switch>
</item>
<item>
<view slot="title">插槽title</view>
</item>
<item title="新闻" icon="/images/icon02.png" url="/pages/yidian/yidian" open-type="switchTab">
</item>
<item title="首页" icon="/images/icon02.png" url="/pages/home/home" >
</item>
<item title="消息" icon="/images/icon02.png" showrslot="{{true}}" itemclass="myitem">
<switch checked="true" slot="right"></switch>
</item>
<!-- <view class="title">自定义组件</view>
<cell bind:cellclick="cellHd" title="中美两国和平相处" num="{{5}}"></cell>
<cell bind:cellclick="cellHd" title="阶级不同立场也不同" ></cell> -->
<!-- <cell cell-class="mycell">
<text slot="pre">💖</text>
<text slot="next">🥗</text>
<text>新冠肺炎消失了</text>
<text>假的</text>
</cell>
<cell>
<text slot="next">🚒</text>
<text slot="pre">💥</text>
<text>工资10倍</text>
<text>鸡蛋50倍</text>
</cell> -->
com.wxss 代码如下:
/* pages/com/com.wxss */
.title{
line-height: 88rpx;
background-color: #f0f0f0;
padding: 0 15rpx;
}
.cell{
color:red;
}
.mycell{
line-height: 120rpx !important;
color:#F70;
}
.myitem{
line-height: 200rpx !important;
background-color: #F0f0f0;
}
wx.getMenuButtonBoundingClientRect() —— 胶囊的边界
wx.getSystemInfoSync(); —— 系统信息(状态栏的高度)
配置自定义状态栏
两个图标
back.svg
<svg class="icon" style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="745"><path d="M670.954667 86.826667L269.44 488.362667a32 32 0 0 0-2.090667 42.965333l2.090667 2.282667L670.933333 935.168a8.533333 8.533333 0 0 0 6.037334 2.496h66.368a8.533333 8.533333 0 0 0 6.037333-14.570667L337.28 511.018667 749.397333 98.901333a8.533333 8.533333 0 0 0-6.037333-14.570666h-66.346667a8.533333 8.533333 0 0 0-6.058666 2.496z" fill="#fff" p-id="746"></path></svg>
home.svg
<svg class="icon" style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1408"><path d="M149.333333 960a85.333333 85.333333 0 0 1-85.333333-85.333333V331.221333a85.333333 85.333333 0 0 1 48.64-77.056l362.666667-172.693333a85.333333 85.333333 0 0 1 73.386666 0l362.666667 172.693333A85.333333 85.333333 0 0 1 960 331.221333V874.666667a85.333333 85.333333 0 0 1-85.333333 85.333333H149.333333zM505.386667 138.24l-2.56 1.024-362.666667 172.693333a21.333333 21.333333 0 0 0-11.989333 16.554667L128 331.221333V874.666667a21.333333 21.333333 0 0 0 18.837333 21.184L149.333333 896h192V661.333333a85.333333 85.333333 0 0 1 85.333334-85.333333h170.666666a85.333333 85.333333 0 0 1 85.333334 85.333333v234.666667h192a21.333333 21.333333 0 0 0 21.184-18.837333L896 874.666667V331.221333a21.333333 21.333333 0 0 0-9.792-17.941333l-2.368-1.322667-362.666667-172.693333a21.333333 21.333333 0 0 0-15.786666-1.024zM597.333333 640h-170.666666a21.333333 21.333333 0 0 0-21.184 18.837333L405.333333 661.333333v234.666667h213.333334V661.333333a21.333333 21.333333 0 0 0-18.837334-21.184L597.333333 640z" fill="#fff" p-id="1409"></path></svg>
// components/nav/nav.js
Component({
/**
* 组件的属性列表
*/
properties: {
"title":{
type:"string",
value:""
},
"color":{
type:"string",
value:"#fff"
}
},
/**
* 组件的初始数据
*/
data: {
statusBarHeight:20,
barHeight:44,
pagesLen:0,
},
lifetimes:{
attached(){
// 获取系统信息
var info = wx.getSystemInfoSync();
console.log(info);
// 更新状态栏的高度
this.setData({statusBarHeight:info.statusBarHeight})
// 胶囊的位置
const res = wx.getMenuButtonBoundingClientRect()
console.log(res);
// 标题栏可以使用的宽度(排除右侧胶囊的位置)
this.setData({titleWidth:res.left});
// 标题栏高度
var barHeight = res.height+(res.top-info.statusBarHeight)*2;
this.setData({barHeight});
// 获取当前页
var pages = getCurrentPages();
console.log(pages);
//更新页面的长度
this.setData({pagesLen:pages.length})
}
},
/**
* 组件的方法列表
*/
methods: {
goBack(){
wx.navigateBack()
},
goHome(){
wx.navigateBack({
// 返回历史的长度为总页面数
delta: this.data.pagesLen,
})
}
}
})
{
"component": true,
"usingComponents": {}
}
<!--components/nav/nav.wxml-->
<view class="nav" style="padding-top: {{statusBarHeight}}px; height: {{barHeight}}px;">
<view class="bar" style="width: {{titleWidth}}px; height: {{barHeight}}px; color: {{color}};">
<view class="btn">
<view class="back" wx:if="{{pagesLen>1}}" bindtap="goBack" ><image src="./images/back.svg" mode="aspectFill"></image></view>
<view class="home" bindtap="goHome"><image src="./images/home.svg" mode="aspectFit"></image></view>
</view>
{{title}} {{pagesLen}}
</view>
</view>
/* components/nav/nav.wxss */
.nav{
background-image: linear-gradient(90deg, rgb(96, 197, 236), rgb(37, 125, 197));
position: sticky;
}
.nav .bar{
box-sizing: border-box;
padding: 0 7px;
display: flex;
align-items: center;
}
.btn{display: flex; align-items: center;}
.btn>view{
height: 40px;
width: 40px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.btn image{
height: 22px;
width: 22px;
align-items: center;
}
.btn .back { border-right: 1rpx solid rgba(255,255,255,.5); height: 22px;}
// pages/navpa/navpa.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
{
"usingComponents": {
"nav":"/components/nav/nav"
},
"navigationStyle":"custom"
}
<nav title="你好渐变标题" color="#fff"></nav>
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fphoto%2F2012-3-2%2Fenterdesk.com-B526ECADD33DBD367676A93E051BA1EC.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671158107&t=aaeb716592b0a54dba6dfb997e761a60"></image>
/* pages/navpa/navpa.wxss */
/* 空 */