小程序学习:自定义组件
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
本文要创建一个效果如下图的组件,由一个心形图片和右上角的数字构成。
1、创建自定义组件
(1)首先在根目录下新建文件夹components,components下新建like文件夹,like下新建index页面;
(2)在 index.json 文件中进行自定义组件声明:
{"component": true
}
(3)在 index.wxml 文件中编写组件模板,在 index.wxss 文件中加入组件样式,它们的写法与页面的写法类似;
编写组件样式时,需要注意以下几点:
- 组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器。
- 组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,如遇,请避免使用。
- 子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。
- 继承样式,如 font 、 color ,会从组件外继承到组件内。
- 除继承样式外, app.wxss 中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)。
#a {
} /* 在组件中不能使用 */
[a] {
} /* 在组件中不能使用 */
button {
} /* 在组件中不能使用 */
.a > .b {
} /* 除非 .a 是 view 组件节点,否则不一定会生效 */
(4)在index.js 文件中,需要使用 Component 来注册组件,并提供组件的属性定义、内部数据和自定义方法:
Component({behaviors: [],// 属性定义(详情参见下文)properties: {myProperty: { // 属性名type: String,//类型(必填)value: ''//属性初始值(选填),默认布尔值初始为false,数字初始为0observer: function(newVal, oldVal, changedPath){//属性值变化时的回调函数(选填),也可以写成在methods段中定义的方法名字符串,如'_propertyChange'//newVal是新设置的数据,oldVal是旧数据}},myProperty2: String // 简化的定义方式},data: {}, // 私有数据,可用于模板渲染lifetimes: {// 生命周期函数,可以为函数,或一个在methods段中定义的方法名attached() { },moved() { },detached() { },},// 生命周期函数,可以为函数,或一个在methods段中定义的方法名attached() { }, // 此处attached的声明会被lifetimes字段中的声明覆盖ready() { },pageLifetimes: {// 组件所在页面的生命周期函数show() { },hide() { },resize() { },},methods: {onMyButtonTap() {this.setData({// 更新属性和数据的方法与更新页面数据的方法类似})},// 内部方法建议以下划线开头_myPrivateMethod() {// 这里将 data.A[0].B 设为 'myPrivateData'this.setData({'A[0].B': 'myPrivateData'})},_propertyChange(newVal, oldVal) {}}})
下面具体实现。
- index.wxml:
<view class="container" bind:tap="onLike"><image src="{{like?yes_url:no_url}}" /><text>{{count}}</text>
</view>
组件中最好不要留有无意义的间距,例如文字的行间距,设置line-height为文字大小可消除行间距。
- index.wxss:
.container{display: flex;flex-direction: row;/* 必须指定宽度,否则会出现移动 *//* width:120rpx; */padding:10rpx;
}.container text{font-size:24rpx;font-family: "PingFangSC-Thin";//苹果手机的默认字体是“苹方”,而安卓是“思源”。color: #bbbbbb;line-height:24rpx;//用于消除文字的上下间距position:relative;//相对定位bottom:10rpx;left:6rpx;
}.container image{width:32rpx;height:28rpx;
}
- index.js:
Component({properties: {like: Boolean,count: Number,readOnly:Boolean},data: {yes_url: 'images/like.png',no_url: 'images/like@dis.png'},methods: {onLike: function (event) {if(this.properties.readOnly){return}let count = this.properties.countcount = this.properties.like ? count - 1 : count + 1this.setData({count: count,like: !this.properties.like})let behavior = this.properties.like ? 'like' : 'cancel'this.triggerEvent('like', {behavior: behavior}, {})}}
})
properties中定义的属性是需要从外部,比如服务器获取的数据;
而data中的数据是从本地加载的,或者是不需要在外部改变的;
但是最终小程序会将properties和data中的数据指向同一个JavaScript对象。
2、使用自定义组件
在需要使用自定义组件的页面的json文件中定义:
{"usingComponents": {"like-cmp": "/components/like/index",}
}
like-cmp是组件的名字。
然后在wxml文件中使用:
<like-cmp bind:like="onLike" like="{{like}}" count="{{count}}" />
自定义组件中的data里的数据是私有的,不能在外部更改,只能被组件自身的wxml文件使用;而properties中的属性可以在外部更改。
3、数据传递的流程
(1)数据从服务器传递到页面的js文件;
(2)通过setData将数据绑定到页面的wxml文件中;
(3)由于使用了自定义组件,数据通过设置组件属性值的方式传递到组件的wxml中。
4、自定义事件的激活与监听
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html
如果在外部某个页面使用自定义组件时,要给该组件添加监听事件,则该事件的返回值里没有组件的数据。这就需要在自定义组件里的methods中的点击事件中触发一个自定义事件like。
methods: {onLike: function (event) {if(this.properties.readOnly){return}let count = this.properties.countcount = this.properties.like ? count - 1 : count + 1this.setData({count: count,like: !this.properties.like})let behavior = this.properties.like ? 'like' : 'cancel'this.triggerEvent('like', {behavior: behavior}, {})}}
其中triggerEvent(’’,{},{})用来触发事件behavior,三个参数指定事件名、detail对象和事件选项;
这时,在外部使用自定义组件时就能得到组件中like属性的值,:
<like-cmp bind:like="onLike" class="like" like="{{like}}" count="{{count}}" />
like的值在event.detail中:
onLike:function(event){let like_or_cancel = event.detail.behavior},
5、组件的生命周期函数
最重要的生命周期是 created、attached、detached ,包含一个组件实例生命流程的最主要时间点。
created:在组件实例刚刚被创建时执行
attached:在组件实例进入页面节点树时执行
ready:在组件在视图层布局完成后执行
moved :在组件实例被移动到节点树另一个位置时执行
detached:在组件实例被从页面节点树移除时执行
error: 每当组件方法抛出错误时执行
6、属性值变化时的回调函数observer
属性值的改变情况可以使用 observer 来监听。目前,在新版本基础库中不推荐使用这个字段,而是使用 Component 构造器的 observers 字段代替,它更加强大且性能更好。
数据监听器详见:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html
比如某一自定义组件要监听来自服务器的数据index,如果index为0~9,要在index前加0,否则不变:
properties: {index:{type: Number,observer:function(newVal, oldVal, changedPath){if (newVal < 10) {this.setData({_index: '0' + newVal})}}}},/*** 组件的初始数据, data 的值也会被页面绑定, 但data的值不可以从组件外部设置*/data: {_index:String//用来接收改变后的index值}
但是,千万不要在observer函数中修改自身的属性,否则就会无限递归。
应该重新设置一个变量:_index。
也可以这样写:
properties: {index:{type: Number,observer:'func'}}}},/*** 组件的初始数据, data 的值也会被页面绑定, 但data的值不可以从组件外部设置*/data: {_index:String//用来接收改变后的index值}methosd: {func(newVal, oldVal, changedPath){if (newVal < 10) {this.setData({_index: '0' + newVal})}
事件在组件中的传递:
如果组件里还有组件,即组件嵌套,则当点击页面的组件时,事件响应会从最底层的组件逐级向上传递,最后传给页面的响应函数。
7、组建的behavior行为
behavior可以实现组件的继承机制。
定义方式和组件一样,把组件Component关键字换成Behavior,可以把几个组件共有的属性、方法等放在一个behavior里.
定义Behavior:
let classicBehavior = Behavior({properties: {type:String,img:String,content:String},data: {}
})export { classicBehavior }
继承Behavior:
properties、data、methods、生命周期函数都可以被组件继承。
在需要继承Behavior的组件里导入:
import {classicBehavior} from '../classic-beh.js'
Component({/*** 组件的属性列表*/behaviors:[classicBehavior],//若要继承多个Behavior,以逗号分隔properties: {},/*** 组件的初始数据*/data: {},/*** 组件的方法列表*/methods: {}
})
说明:
(1)组件继承符合一般继承规则,如果子类和父类有同名属性,子类属性会覆盖父类属性。
(2)多继承时,且子类没有同名属性而几个父类之间有同名属性是,写在 behaviors:[a, b, c]括号中最后一个会覆盖其他的。
(3)生命周期函数不会有覆盖情况,小程序会依次执行父类的生命周期函数,再执行子类的生命周期函数。
8、组件的hidden属性
当需要组件切换显示/隐藏时,可以使用wx:if条件渲染,也可以给组件加hidden属性
wx:if vs hidden
因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。
同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。
一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。
因此一般使用hidden更好。
但是在自定义组件里使用hidden无效。
所以为了让自定义组件也能使用hidden,可以在组件的properties里加入hidden属性,然后在组件的wxml外部的view标签内添加hidden属性:
properties: {hidden:flase},
<view hidden={{hidden}} class="classic-container"><image src="{{img}}" class="classic-img"></image><image class='tag' src="data:images/essay@tag.png" /><text class="content">{{content}}</text>
</view>
在使用该组件时:
wx:if 写法:
<movie-cmp wx:if="{{classic.type==100}}" img="{{classic.image}}" content="{{classic.content}}" />
hidden写法:
<movie-cmp hidden="{{classic.type!=100}}" img="{{classic.image}}" content="{{classic.content}}" />
注:如果使用hidden属性,组件不会完整的执行一次生命周期,例如组件生命周期的detach()函数不会触发。
所以如果要执行detach()函数,还应该使用wx:if
9、组件中样式的复用
如果几个组件有相同的样式,则可以通过@import的方式导入共有的样式,实现样式的复用。这是template里的做法。
例如:
@import "../common.wxss";
后面一定要加封号。
10、组件间的通信
父子组件间的基本通信方式有以下几种。
- WXML 数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据(自基础库版本 2.0.9
开始,还可以在数据中包含函数)。具体在 组件模板和样式 章节中介绍。 - 事件:用于子组件向父组件传递数据,可以传递任意数据。
- 如果以上两种方式不足以满足需要,父组件还可以通过 this.selectComponent
方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。
11、点击组件进行页面跳转
点击组件进行页面跳转的事件函数可以直接写在组件里,而不用写在Page页面里,这样就不用在组件和页面之间传递参数了。
例如对于组件book,其bindtap事件写在methods里:
methods: {onTap(event){const bid = this.properties.book.idwx.navigateTo({url:`/pages/book-detail/book-detail?bid=${bid}`})// 降低了组件的通用性// 非常方便// 服务于当前的项目 项目组件// }}
12、组件wxml的slot(插槽)
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html
在组件的wxml中可以包含 slot 节点,用于承载组件使用者提供的wxml结构。
默认情况下,一个组件的wxml中只能有一个slot。需要使用多slot时,可以在组件js中声明启用。
Component({options: {multipleSlots: true // 在组件定义时的选项中启用多slot支持},properties: { /* ... */ },methods: { /* ... */ }
})
此时,可以在这个组件的wxml中使用多个slot,以不同的 name 来区分。
<!-- 组件模板 -->
<view class="wrapper"><slot name="before"></slot><view>这里是组件的内部细节</view><slot name="after"></slot>
</view>
使用时,用 slot 属性来将节点插入到不同的slot上。
<!-- 引用组件的页面模板 -->
<view><component-tag-name><!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 --><view slot="before">这里是插入到组件slot name="before"中的内容</view><!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 --><view slot="after">这里是插入到组件slot name="after"中的内容</view></component-tag-name>
</view>
其中,slot的样式可以写在页面的wxss里。
例如,将下图的tag组件后面加上数字:
组件wxml:
<view class="container tag-class "><slot name="before"></slot><text >{{text}}</text><slot name="after"></slot>
</view>
页面wxml:
<v-tag tag-class="{{tool.highlight(index)}}" text="{{item.content}}"><text class="num" slot="after">{{'+'+item.nums}}</text></v-tag>
页面wxss中定义slot样式:
.num {margin-left: 10rpx;font-size: 22rpx;color: #aaa;
}
13、外部样式类externalClass
有时,组件希望接受外部传入的样式类,比如从页面传入样式到组件。此时可以在 Component 中用 externalClasses 定义段定义若干个外部样式类。
组件的js文件:
Component({externalClasses: ['tag-class']
})
组件wxml:
<view class="container tag-class "><slot name="before"></slot><text >{{text}}</text><slot name="after"></slot>
</view>
页面wxml:
<v-tag tag-class="ex-tag" text="{{item.content}}">
页面wxss:
.ex-tag {background-color: #fffbdd;
}
注意:在同一个节点上使用普通样式类和外部样式类时,比如container和tag-class,两个类的优先级是未定义的,因此最好避免这种情况。
可以使用!important来使外部样式强制覆盖普通样式。
页面wxss:
.ex-tag {background-color: #fffbdd !important;
}
14、区分同一页面下的多个相同组件的点击事件
当一个页面加载了多个相同的组件,当点击其中一个,需要一个id来判断用户点击了哪个组件。
在页面的XML中,给每个组件加入一个data-id:
<van-button data-id="{{item._id}}" size="small" type='primary' plain bind:click='viewItem'>详情</van-button>
这样在每个组件的点击事件中就可以取出该id,从而知道用户点击了哪个组件的按钮:
viewItem:function(event){//console.log(event);var id = event.currentTarget.dataset.id;wx.navigateTo({url: '../bookDetail/bookDetail?id='+id,})},
小程序学习:自定义组件相关推荐
- 小程序如何自定义组件
之前在工作中的时候遇到过了小程序自定义组件的问题,所以就想和大家分享一下小程序如何自定义组件.首先让我们来了解一下组件是什么,为什么要使用组件.组件的官方解释是说开发者可以把页面内的功能模块抽象成自定 ...
- 微信小程序的自定义组件(2)
文章目录 6. 组件的生命周期 7. 组件所在页面的生命周期 8. 组件插槽 9. 组件父子组件间的通信 10. 组件-behaviors 6. 组件的生命周期 created attached re ...
- 微信小程序(自定义组件)
文章目录 自定义组件 创建自定义组件 引用组件 组件和页面的区别 自定义组件的样式 样式隔离 修改样式隔离选项 组件的data和methods 组件的properties 数据监听 使用纯数据字段提升 ...
- 微信小程序 配置自定义组件代码按需注入 lazyCodeLoading
微信小程序 配置自定义组件代码按需注入 lazyCodeLoading 官方网址 在app.json最后一行加上,就可以了. "lazyCodeLoading": "re ...
- 小程序-实现自定义组件以及自定义组件间的通信
前言 对于组件的封装,在小程序当中对于多个页面的复用有着重要的作用,小程序中注册的每个页面都是独立的 页面的显示view层与逻辑层是通过data进行绑定关联,若需要更改页面中的数据,则通过setDat ...
- 微信小程序之自定义组件的使用、介绍、案例分享
微信小程序自定义组件介绍 自定义组件发开文档 类似vue或者react中的自定义组件, 小程序允许我们使用自定义组件的方式来构建页面. 自定义组件的使用 1. 创建组件(js,json,wxml,wx ...
- 微信小程序之自定义组件(微信小程序完结)
微信小程序 自定义组件 类似vue或者react中的自定义组件, 小程序允许我们使用自定义组件的方式来构建页面. 1.1 创建自定义组件 类似于页面,一个自定义组件由 json, wxml, wxss ...
- 微信小程序中自定义组件
文章目录 小程序项目 app.json pages/index/index.wxml pages/index/index.wxss pages/index/index.js 自定义组件 compone ...
- 微信小程序入门与实战之初识小程序的自定义组件
文章列表顶部轮播图跳转 为每个轮播图设置点击响应函数,为其设置参数为文章的id 小程序tabBar选项卡配置基础 我们在app.json配置tabBar选项卡: "tabBar": ...
- 小程序新手引导自定义组件
//WXML <!-- 组件遮罩引导 --> <view class='page' hidden='{{modalHidden}}'><view class='conts ...
最新文章
- 20天拿到美团快手小米搜狐跟谁学offer
- Google App Engine给我们带来了什么?
- 一个使用react native实现的短视频APP
- 销售订单屏幕增强及功能增强
- 北邮OJ 2016 网预-Square Coins
- 计算机 编程 教程 pdf,计算机专业教程-第3章编程接口介绍.pdf
- Arduino的串口结束符及串口缓冲区
- 同济大学土木工程学院招收2名秋季入学全日制博士生
- arm linux远程桌面win7卡顿,主编解答win7系统使用远程桌面出现卡顿的恢复方法
- java 知乎面试题_2019最新Java面试题,常见面试题及答案汇总(208道)
- XML入门的常见问题
- Altium designer学习(二)pcb库不求人——立创商城导出封装库
- ftp 命令访问 ftp服务器
- 从前端到全栈-基础能力-js-异步编程
- 关于苹果的iOS cercertificate的创建问题
- python实现工具exe自动化
- 淘宝接口 TopAPi(转)
- 成都Java培训机构太多,该怎样选择呢?
- 网络常识:公网、私网、内网等
- mysql插入数据的时候,有就更新没有就新增