开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。

本文要创建一个效果如下图的组件,由一个心形图片和右上角的数字构成。

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,})},

小程序学习:自定义组件相关推荐

  1. 小程序如何自定义组件

    之前在工作中的时候遇到过了小程序自定义组件的问题,所以就想和大家分享一下小程序如何自定义组件.首先让我们来了解一下组件是什么,为什么要使用组件.组件的官方解释是说开发者可以把页面内的功能模块抽象成自定 ...

  2. 微信小程序的自定义组件(2)

    文章目录 6. 组件的生命周期 7. 组件所在页面的生命周期 8. 组件插槽 9. 组件父子组件间的通信 10. 组件-behaviors 6. 组件的生命周期 created attached re ...

  3. 微信小程序(自定义组件)

    文章目录 自定义组件 创建自定义组件 引用组件 组件和页面的区别 自定义组件的样式 样式隔离 修改样式隔离选项 组件的data和methods 组件的properties 数据监听 使用纯数据字段提升 ...

  4. 微信小程序 配置自定义组件代码按需注入 lazyCodeLoading

    微信小程序 配置自定义组件代码按需注入 lazyCodeLoading 官方网址 在app.json最后一行加上,就可以了. "lazyCodeLoading": "re ...

  5. 小程序-实现自定义组件以及自定义组件间的通信

    前言 对于组件的封装,在小程序当中对于多个页面的复用有着重要的作用,小程序中注册的每个页面都是独立的 页面的显示view层与逻辑层是通过data进行绑定关联,若需要更改页面中的数据,则通过setDat ...

  6. 微信小程序之自定义组件的使用、介绍、案例分享

    微信小程序自定义组件介绍 自定义组件发开文档 类似vue或者react中的自定义组件, 小程序允许我们使用自定义组件的方式来构建页面. 自定义组件的使用 1. 创建组件(js,json,wxml,wx ...

  7. 微信小程序之自定义组件(微信小程序完结)

    微信小程序 自定义组件 类似vue或者react中的自定义组件, 小程序允许我们使用自定义组件的方式来构建页面. 1.1 创建自定义组件 类似于页面,一个自定义组件由 json, wxml, wxss ...

  8. 微信小程序中自定义组件

    文章目录 小程序项目 app.json pages/index/index.wxml pages/index/index.wxss pages/index/index.js 自定义组件 compone ...

  9. 微信小程序入门与实战之初识小程序的自定义组件

    文章列表顶部轮播图跳转 为每个轮播图设置点击响应函数,为其设置参数为文章的id 小程序tabBar选项卡配置基础 我们在app.json配置tabBar选项卡: "tabBar": ...

  10. 小程序新手引导自定义组件

    //WXML <!-- 组件遮罩引导 --> <view class='page' hidden='{{modalHidden}}'><view class='conts ...

最新文章

  1. 20天拿到美团快手小米搜狐跟谁学offer
  2. Google App Engine给我们带来了什么?
  3. 一个使用react native实现的短视频APP
  4. 销售订单屏幕增强及功能增强
  5. 北邮OJ 2016 网预-Square Coins
  6. 计算机 编程 教程 pdf,计算机专业教程-第3章编程接口介绍.pdf
  7. Arduino的串口结束符及串口缓冲区
  8. 同济大学土木工程学院招收2名秋季入学全日制博士生
  9. arm linux远程桌面win7卡顿,主编解答win7系统使用远程桌面出现卡顿的恢复方法
  10. java 知乎面试题_2019最新Java面试题,常见面试题及答案汇总(208道)
  11. XML入门的常见问题
  12. Altium designer学习(二)pcb库不求人——立创商城导出封装库
  13. ftp 命令访问 ftp服务器
  14. 从前端到全栈-基础能力-js-异步编程
  15. 关于苹果的iOS cercertificate的创建问题
  16. python实现工具exe自动化
  17. 淘宝接口 TopAPi(转)
  18. 成都Java培训机构太多,该怎样选择呢?
  19. 网络常识:公网、私网、内网等
  20. mysql插入数据的时候,有就更新没有就新增

热门文章

  1. 数据结构学习(冒泡、选择、插入、快速排序)
  2. [益智]:猴子搬香蕉
  3. 从Spring源码探究IOC初始化流程
  4. 校招软件测试面试问题总结
  5. 如何还原恢复格式化后的数据文件?
  6. mysql对比时间段百分比_如何使用MySQL计算每日最高价格变化百分比?
  7. Java修炼——手写服务器项目
  8. Ambari——大数据平台的搭建利器(一)
  9. 一举拿下阿里、字节、美团、京东、虾皮offer
  10. Shiro限制登录尝试次数(适用于单节点)