自定义组件

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

创建自定义组件

(自定义组件需要重新开一个文件夹,然后再在其他页面引用,可以理解成封装一个组件,一个组件放在一个文件夹里)类似于页面,一个自定义组件由 json wxml wxss js 四个文件组成。要编写一个自定义组件,首先需要在 json 文件中进行自定义组件声明(将 component 字段设为 true 可将这一组件设为自定义组件):

{"component": true
}

同时,还要 wxml 文件中编写组件模板,在 wxss 文件中加入组件样式,他们的写法与页面的写法类似。具体细节和注意事项参见 。

<!-- 这是自定义组件的内部wxml结构 -->
<view class="inner">{{innerText}}</view>
<slot></slot>
/* 这里的样式只应用于这个自定义组件 */
.inner{color:red;
}
注意:在组件wxss中不应使用ID选择器,属性选择器和标签选择器。

在自定义组件的js文件中,需要使用 Component()来注册组件,并提供组件的属性定义,内部数据和自定义方法。
组件的属性值和内部数据将被用于组件 wxml 的渲染,其中,属性值是可由组件外部传入的。更多细节参见 Component构造器。

Component({properties:{//这里定义了innerText属性,属性值可以在组件使用时指定innerText:{type:String,value:'default value',}},data:{//这里是一些组件内部数据someData:{}},methods:{// 这是一个自定义方法customMethod:function(){}}
})
使用自定义组件

使用已注册的自定义组件前,首先要在页面的json 文件中进行应用声明。此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径:


//需要应用的页面json文件
{"usingComponents":{"component-tag-name":"path/to/the/custom/component"//前面是组件名,后面是封装的文件的地址}
}

这样,在页面的 wxml 中就可以像使用基础组件一样使用自定义组件。节点名即自定义组件的标签名,节点属性即传递给组件的属性值。

<view>
<!-- 以下是对一个自定义组件的引用 --><component-tag-name inner-text="Some text"></component-tag-name>
</view>

(微信开发者工具有一个代码示例片段)
自定义组件的 wxml 节点结构在与数据结合之后,将被插入到引用位置内。

细节注意事项

一些需要注意到细节:

  • 因为 wxml 节点标签名只能是小写字母,中划线和下划线的组合,所以自定义组件的标签名也之只能包含这些字符。
  • 自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用 usingComponents 字段)。
  • 自定义组件和页面所在项目根目录名不能以 “ wx- ” 为前缀,否则会报错。

注意,是否在页面文件中使用 usingComponents 会使得页面的 this 对象的原型稍有差异 ,
包括:

  • 使用 usingComponents 页面的原型于不使用时不一致,即 Object.getPrototypeOf(this) 结果不同。
  • 使用 usingComponent 时会多一些方法,如 selectComponent 。
  • 出于性能考虑,使用 usingComponents 时,setData 内容不会被直接深复制,即 this.setData({field:obj}) 后 this.data.field === obj 。(深复制会在这个值被组件间传递时发生。)

如果页面比较复杂,新增或删除 usingComponents 定义段时建议重新测试一下。

组件模板和样式

类似于页面,自定义组件拥有自己的 wxml 模板和 wxss 样式。

组件模板

组件模板的写法与页面模板相同。组件模板于组件数据结合后生成的节点树,将被插入到组件的引用位置上。
在组件模板中可以提供一个 < slot > 节点,用于承载组件引用时提供的子节点,用于承载组件引用时提供的子节点。

<!-- 组件模板 -->
<view class=“wrapper”><view>这里是组件的内部节点</view><slot></slot>
</view>
<!-- 引用组件的页面模板 -->
<view><component-tag-name><!--这部分内容将被放置在组件 <slot> 的位置上--><view>这里是插入到组件 slot 中的内容</view></component-tag-name>
</view>

注意,在模板中引用到的自定义组件及其对应的节点名需要在 json 文件中显式定义,否则会被当作一个无意义的节点,除此以外,节点名也可以被声明为抽象节点。

模板数据绑定

与普通的 wxml 模板类似,可以使用数据绑定,这样就可以向子组件的属性传递动态函数。

<!-- 应用组件的页面模板 -->
<view><component-tag-name prop-a=“{{dataFieldA}}” porp-b=“{{dataFieldB}}”><!-- 这部分内容将被放置在组件 <slot> 的位置上--><view>这里是插入到组件slot中的内容</view></component-tag-name>
</view>

在以上例子中,组件的属性 propA 和 propB 将收到页面传递的数据。页面可以通过 setData 来改变绑定的数据字段。
注意:这样的数据绑定只能传递 JSON 兼容数据。还可以在数据中包含函数(但这些函数不能在 WXML 中直接调用,只能传递给子组件)。

组件wxml的slot

在组件的 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=“defore” 中的内容</view><!--这部分内容将被放置在组件 <slot name="after"> 的位置上 --><view slot="after">这里是插入到组件slot name=“after” 中的内容</view></component-tag-name>
</view>
组件样式

组件对应 wxss 文件的样式,只为组件 wxml 内的节点生效。编写组件样式时,需要注意以下

  • 组件和引用组件的页面不能使用 id 选择器(#a),属性选择器( [a] )和标签名选择器,请改变class选择器。
  • 组件和引用组件的页面中使用后代选择器(.a,.b)在一些极端情况下会有非预期的表现,
  • 子选择选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。
  • 继承样式,如 font ,color,会从组件外继承到组件内。
  • 出继承组件外,app,wxss 中的样式,组件所在页面的样式对自定义
#a {} /*在组件中不能使用*/
[a] {} /*在组件中不能使用*/
button {} /*在组件中不能使用*/
.a>.b {} /*除非 .a 是 view 组件节点,否则不一定会生效 */

除此以外,组件可以指定他所在节点的默认样式,使用 :host 选择器

/* 组件 custom-component.wxss */
:host {color: yellow;
}
<csutom-component>这段文本是黄色的</csutom-component>
组件样式隔离

默认情况下,自定义组件的样式只受到自定义组件 wxss 影响。除非以下两种情况:

  • app.wxss 或页面的 wxss 中使用了标签名选择器(或一些其他特殊选择器)来指定样式,这些选择器会影响到页面和全部组件。通常情况下这是不推荐的做法。
  • 指定特殊的样式个理选项 styleIsolation 。
Component({option:{styleIsolation: 'isolated'}
})

styleIsolation ,支持以下取值:

  • isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响 ;
  • apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
  • shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。(这个选项在插件中不可用。)
使用后两者时,请务必注意组件间样式的相互影响。

如果这个 Component 构造器用于构造页面,则默认值为 shared ,且还有以下的几个选项:

  • page-isolated 表现在这个页面禁用 app.wxss ,同时,页面的wxss 不会影响到其他自定义组件;
  • page-apply-shared 表示在这个页面禁用 app.wxss ,同时,页面 wxss 样式不会影响到其他自定义组件,但设为 shared 的自定义组件会影响到页面;
  • page-shared 表示在这个页面禁用 app.wxss 同时,页面 wxss 样式会影响到其他设为 apply-shared 或 shared 的自定义组件,也会收到设为 shared 的自定义组件的影响。

在Component 的 options 中设置 addGlobalClass :true 。这个选项等价于设置 styleIsolation:apply-shared ,但设置了 styleIsolation 选项后这个选项会失效。

/* 组件 custom-component.js*/
Component({options:{addGlobalClass:true,}
})
<!-- 组件 custom-component.wxml-->
<text class="red-text">这段文本的颜色由‘app.wxss’和页面‘wxss’中的样式定义来决定</text>
/* app.wxss*/
.red-text {color:red;
}
外部样式类

有时,组件希望接受外部传入的样式类。此时可以在 Component 中用 wxternaClasses 定义段定义若干个外部样式类。
这个特性可以用于现实类似于 view 组件的 hover-class 属性:页面可以提供一个样式类,赋予 view 的 hover-class ,这个样式类本身写在页面中而非 view 组件的实现中。

注意:在同一个节点上使用普通样式类和外部样式类,两个类的优先级是未定义的,因此最好避免这种情况。
/* 组件 custom-component.js*/
Component({externalClasses:['my-class']
})
<!-- 组件 custom-component.wxml-->
<custom-component class="my-class">这段文本的颜色由组件外的 class 决定</custom-component>

这样,组件的使用者可以指定这个样式类对应的 class ,就像使用普通属性一样。

<!-- 页面的 wxml-->
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />
<custom-component my-class="red-text largen-tex" />
.red-text{color:red;
}
.large-text{font-size: 1.5em;
}

Component 构造器

Component 构造器可用于定义组件。调用 Component 构造器时可以指定组件的属性,数据,方法等。

Component({behaviors:[],properties:{myPropertty:{ // 属性名type:string,value:''},myProperty2: String //简化的定义方法},data:{}, //私有数据,可用于模板渲染lifetimes:{//生命周期函数,可以为函数,或一个在 methods 段中定义的方法名。attached: function () {},moved: function () {},detached: function () {},},//生命周期函数,可以为函数,或一个在 methods 段中定义的方法名attached:function (){},//此处 attache 的声明会被lifetime 字段中的声明覆盖 ready:function (){}, pageLifetiimes:{show:function () {},hide:function () {},resize: function () {},},methods:{onMyButtonTap:function (){this.setData({//更新属性和数据的方式于更行页面数据的方法类似})},//内部方法建议以下划线开头_myPrivateMethod:function(){//这里将 data.A[0].B 设为 ‘myPrivateData’this.setData({'A[0].B':'myPrivateData'})},_propertyChange:function(newVal,oldVal){}}
})
使用 Component 构造器构造页面

事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用 Component 构造器构造,拥有与普通组件一样的定义段与实例方法。但此时要求对应 json 文件中包含 usingComponent 定义段。
此时,组件的属性可以用于接受页面的参数,如访问页面 /page/index/index?paramA=123&paramB=xyz ,如果声明有属性 paramA 或 paramB,则他们会被赋值为 123 或者 xyz 。
页面的声明周期方法 (即 on 开头的方法),应写在 methods 定义段中。

Component({properties:{paramA:Number,paramB:String,},methods:{onLoad:function(){this.data.paramA //页面参数 paramA 的值this.data.paramB //页面参数 paramB 的值}}
})

使用 Component 构造器来构造页面的一个好处是可以使用 behavios 来提取所有页面中公用的代码段。
例如,在所有页面被创建和销毁时都要执行同一段代码,就可以把这段代码提取到 behaviors 中。

//page-common-behavior.js
module.exports = Behavior({attached:function (){console.info('Page loaded!')},detached:function() {console.info('Page unloaded!')}
})
// 页面 A
var pageCommonBehavior = require('./page-commmon-behavior')
Component({behaviors:[pageCommonBehavior],data:{ /* ... */ },methods:{ /* ... */ },
})
//页面 B
var pageCommonBehavior = require('./page-common-behavior')
Component({behaviors:[pageCommonBehavior],data:{ /* ... */ },methods:{ /* ... */ },
})
组件间通信与事件
组件间通信

组件间的基本通信方式。

  • wxml数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据。
  • 事件:用于子组件向父组件传递数据,可以传递任意数据。
  • 如果以上两种方式不足以满足需要,父组件话可以通过 this.selectComponent 方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。
监听事件

事件系统时组件间通信的主要方式之一。自定义组件可以触发任意的事件,引起组件的页面可以监听这些事件。

<!-- 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法-->
<component-tag-name bindmyevent="onMyEvent" />
<component-tag-name bind:wdmyevent="onMyEvent" />
Page({onMyEvent:function(e){e.detail //自定义组件触发事件时提供的datail对象}
})
触发事件

自定义组件触发事件时,需要使用 triggerEvent 方法,指定事件名,detail对象和事件选项:

<!-- 在自定义组件中 -->
<button bindTop="onTap">点击这个按钮将触发“myevent”事件</button>
Component({properties:{},methods:{onTap:function(){var myEventDetail = {}//detail对象,提供给事件监听函数var myEventOption = {}//触发事件的选项this.triggerEvent('myevent',myEventDetail,myEventOption)}}
})

后面例子

选项名 描述
bubbles 事件是否冒泡
composed 事件是否可以穿越组件边界,为 false 时,事件将只能在引用组件的节点鼠树上触发,不进入其他任何组件内部
capturePhase 事件是否拥有捕获阶段
// 页面 page.wxml
<another-component bindcustomevent="pageEventListener1"><my-component bindcustomevent="pageEventListener2"></my-component>
</another-component>//组件 another-component.wxml
<view bindcustomevent="anotherEventListener"><slot />
</view>
//组件 my-component.wxml
<view bindcustomevent="myEventListener"><slot />
</view>

triggerEvent 类似于 addEventListner 后面是可选参数

//组件 my-component.js
Component({methods:{onTap:function(){this.triggerEvent('customevent',{})//只会触发 pageEventListener2this.triggerEvent('customevent', {}, { bubbles: true }) // 会依次触发 pageEventListener2 、 pageEventListener1this.triggerEvent('customevent', {}, { bubbles: true, composed: true }) // 会依次触发 pageEventListener2 、 anotherEventListener 、 pageEventListener1}}
})

组件声明周期

组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的事件点或遇到一些特殊的框架事件是被自动触发。
其中,最重要的生命周期是 created attached detached ,包含一个组件实例生命流程的是主要时间点。

  • 组件实例刚刚被创建好,created 生命周期被触发,此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data 。此时还不能调用 setData .通常情况下,这个生命周期只应该用于给组件 this 添加一些二自定义属性字段。(类似于react中第一个constructor,用于添加自定义)
  • 在组件完全初始化完毕,进入页面节点数之后,attached 生命周期,此时,this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。
  • 在组件离开页面节点树后, detached 生命周期被触发。推出一个页面时,如果组件还在页面节点树中,则 datched 会被触发。
定义生命周期方法

生命周期方法可以直接定义再== Component 构造器的第一级参数中,也可以在 lifetimes 字段内进行声明 (这是推荐的方式,其优先级最高)==

Component({lifetimes:{attched:function() {//在组件实例进入页面节点树时执行},detached: function() {//在组件实例被从页面节点数移除时执行},},//以下时旧式的定义方式attched: function(){//在组件实例进入页面节点数时执行},detached:function() {//在组件实例被从页面节点树移除时执行},.....
})

在 behaviors 中也可以编写生命周期方法,同时不会与其他 behaviors 中的同名生命周期相互覆盖。但要注意,如果一个组件多次直接或间接引用同一个 behavior ,这个 behavior 中的生命周期函数在一个执行时机内只会执行一次。

可用的全部生命周期如下表所示

生命周期 描述
created 组件实例刚刚被创建时执行
attached 在组件实例进入页面节点树时执行
ready 在组件的视图层布局完成后执行
moved 在组件实例被移动到节点树另一个位置时执行
datached 在组件实例被从页面节点树移除时执行
error 每当组件方法抛出错误时执行
组件所在页面的生命周期

还有一些特殊的生命周期。它们并非与组件有很强的关联,但有时组件需要获知,以便组件你内部处理。这样的生命周期称为“组件所在页面的生命周期”,在 pageLifetimes 定义段中定义。其中可用的生命周期包括:

生命周期 描述
show 组件所在的页面被展示时执行
hide 组件所在的页面被隐藏时执行
reszie 组件所在的页面尺寸变化时执行
Component({pageLiftimes:{//页面被展示},hide:function() {//页面被隐藏},resize: function(size){//页面尺寸变化}
})
behavior

behaviors 是用于组件间代码共享的特性,类似于一些编程语言中的 “mixins”或“traits”。
每个 behavior 可以包含一组属性,数据,生命周期函数和方法。组件引用它时,它的属性,数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。每个组件可以引用多个 behavior ,behavior 也可以引用其他 bahavior 。

组件中使用

组件引用时,在 behaviors 定义段中将他门逐个列出

// my-component.js
var myBehavior = require('my-behavior')
Component({behaviors:[myBehavior],properties:{myProperty:{type:String}},data:{myData:{}},attached: function(){},methods:{myMethod: function(){}}
})

在上例中,my-component 组件定义中加入了 my-behavior ,而 my-behavior 中包含有 myBehaviorProperty 属性 ,myBehaviorData 数据字段,myBehaviorMethod 方法和一个 attached 生命周期函数 。
这将使得 my-component 中最终包含myBehaviorPorperty,myProperty两个属性, myBehaviorData,myData 两个数据字段,和myBehaviorMethod,myMetho 两个方法。
当组件触发 attached 生命周期时,会依次触发 my-behavior 中的 attched 生命周期函数和my-component 中的 attached 生命周期函数。

字段的覆盖和组合规则

组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下:

  • 如果有同名的属性和方法,组件本身的属性或方法会覆盖 behavior 中的属性和方法
  • 如果有同名的数据字段,如果数据是对象类型,会进行对象合并,如果是非对象类型则会进行相互覆盖;
  • 生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用。如果同一个 behavior 被一个组件多次引用,它定义的生命周期函数只会被执行一次。
内置 behaviors

自定义组件可以通过引用内置的 behavior 来获得内置组件的一些行为。

Component({behaviors:['wx://form-field']
})

在上例中,wx://form-field 代表一个内置 behavior ,他使得这个自定义组件有类似于表单控件的行为。
内置 behavior 往往会为组件添加一些属性。在没有特殊说明时,组件可以覆盖这些属性来改变它的 type 或添加 observer 。

wx.//form-field

使自定义组件有类似与表单控件的行为。form 组件可以识别这些自定义组件,并在 submit 事件中返回组件的字段名及其对应字段值,这将为它添加以下两个属性。

属性名 描述
name 在表单中字段名
value 在表单中的字段值
wx://component-export

这个定义段可以用于指定组件被 selectComponent 调用时的返回值。
未使用这个定义段时,selectComponent 将返回自定义组件的 this (插件的自定义组件将返回 null )。使用这个定义段时,将以这个定义段的函数返回值代替。

//自定义组件 my-component 内部
Component({behaviors:['wx://component-export'],export(){return {myField:'myValue'}}
})
<my-component id="the-id" />
this.selectComponent('#the-id')//等于{myField:‘myValue’}
组件间关系

定义和使用组件间关系

<custom-ul><custom-li> item 1 </custom-li><custom-li> item 2 </custom-li>
<custom-ul>

这个例子中, custom-ul 和 custom-li 都是自定义组件,它们有相互间的关系,相互间的通信往往比较复杂。此时在组件定义时加入 relations 定义段,可以解决这样的问题。示例:

// path/to/custom-ul.js
Component({relations: {'./custom-li': {type: 'child', // 关联的目标节点应为子节点linked: function(target) {// 每次有custom-li被插入时执行,target是该节点实例对象,触发在该节点attached生命周期之后},linkChanged: function(target) {// 每次有custom-li被移动后执行,target是该节点实例对象,触发在该节点moved生命周期之后},unlinked: function(target) {// 每次有custom-li被移除时执行,target是该节点实例对象,触发在该节点detached生命周期之后}}},methods: {_getAllLi: function(){// 使用getRelationNodes可以获得nodes数组,包含所有已关联的custom-li,且是有序的var nodes = this.getRelationNodes('path/to/custom-li')}},ready: function(){this._getAllLi()}
})
// path/to/custom-li.js
Component({relations: {'./custom-ul': {type: 'parent', // 关联的目标节点应为父节点linked: function(target) {// 每次被插入到custom-ul时执行,target是custom-ul节点实例对象,触发在attached生命周期之后},linkChanged: function(target) {// 每次被移动后执行,target是custom-ul节点实例对象,触发在moved生命周期之后},unlinked: function(target) {// 每次被移除时执行,target是custom-ul节点实例对象,触发在detached生命周期之后}}}
})
注意:必须在两个组件定义中都加入 relation 定义,否则不会生效。
关联一类组件

有时,需要关联的是一类组件

<custom-form><view>input<custom-input></custom-input></view><custom-submit> submit </custom-submit>
</custom-form>

custom-form 组件想要关联 custom-input 和 custom-submit 两个组件。此时,如果这两个组件都有同一个 behavior;

// path/to/custom-form-controls.js
module.exports = Behavior({// ...
})
// path/to/custom-input.js
var customFormControls = require('./custom-form-controls')
Component({behaviors: [customFormControls],relations: {'./custom-form': {type: 'ancestor', // 关联的目标节点应为祖先节点}}
})
// path/to/custom-submit.js
var customFormControls = require('./custom-form-controls')
Component({behaviors: [customFormControls],relations: {'./custom-form': {type: 'ancestor', // 关联的目标节点应为祖先节点}}
})

则在 relation 关系定义中,可使用这个behavior 来代替组件路径作为关联的目标节点:

// path/to/custom-form.js
var customFormControls = require('./custom-form-controls')
Component({relations: {'customFormControls': {type: 'descendant', // 关联的目标节点应为子孙节点target: customFormControls}}
})
relation 定义段

relations 定义段包含目标组件路径及其对应选项,可包含的选项见下表。

选项 描述
type 目标组件的相对关系,可选的值为 parent 、 child 、 ancestor 、 descendant
linked 关系生命周期函数,当关系被建立在页面节点树中时触发,触发时机在组件attached生命周期之后
linkChanged 关系生命周期函数,当关系在页面节点树中发生改变时触发,触发时机在组件moved生命周期之后
unlinked 关系生命周期函数,当关系脱离页面节点树时触发,触发时机在组件detached生命周期之后
target 如果这一项被设置,则它表示关联的目标节点所应具有的behavior,所有拥有这一behavior的组件节点都会被关联
数据监听器

数据监听器可以用于监听和响应任何属性和数据字段的变化

使用数据监听器

有时,在一些数据字段别setData设置时,需要执行一些操作。
例如, this.data.sum 永远是 this.data.numberA 与 this.data.numberB 的和。

Component ({attached: function(){this.setData({numberA:1,numberB:2,})},onservers:{'numberA,numberB': function(numberA,numberB){//在 numberA 或者 numberB 被设置时,执行这个函数this.setData({sum: numberA + numberB})}}
})
监听字段语法

数据监听器支持监听属性或内部数据的变化,可以同时监听多个。一次 setData 最多触发每个监听器一次。
同时,监听器可以监听组函数字段

Component({observers:{'some.subfield':function(subfield){//使用 setData 设置 this.data.some 也会触发//(除此之外,使用 setData 设置 this.data.some 也会触发)subfield === this.data.some.subfield},'arr[12]': function(arr12){//使用 setData 设置 this.data.arr[12] 时触发//(除此以外,使用 setData 设置 this.data.arr 也会触发)arr12 === this.data.arr[12]},}
})

如果需要监听所有子数据字段的变化,可以使用通配符 **

Component({observers:{'some.field.**': function (field){// 使用 setData 设置 this.data.some.field 本身或其下任何子数据字段时触发// (除此以外,使用 setData 设置 this.data.some 也会触发)field === this.data.some.field},},attached: function() {//这样也会触发上面的 observethis.setData({'some.field':{ /* ... */}})//这样也会触发上面的 observerthis.setData({'some.field.xxx':{/* ... */}})// 这样还是会触发上面的 observerthis.setData({'some':{/* ... */}})}
})

特别的,仅使用通配符 ** 可以监听全部 setData

Compoennt({observers: {'**':function(){//每次 setData 都触发},},
})

Bugs & Tips :

  • 数据监听器监听的是 setData 涉及到的数据字段,即使这些数据字段的值没有发生变化,数据监听器依旧会出触发。
  • 如果在数据监听数据监听器中使用 setData 设置本身监听的数据字段,可能会导致死循环,需要特别留意。
  • 数据监听器和属性的 observer 相比,数据监听器更强大且通常具有更好的性能。

纯数据字段

纯数据字段是一些不用于界面渲染的 data 字段,可以用于提升页面更新性能。

组件数据中的纯数据字段

有些情况下,某些 data 中的字段 (包括 setData 设置的字段) 既不会展示在其他页面上,也不会传递给其他组件,仅仅在当前组件内部使用。
此时,可以指定这样的数据字段为“纯数字字段” ,它们将仅仅被记录在 this.data 中,而不参与任何界面渲染过程,这样有助于提高页面更新性能。
指定 “纯数据字段” 的方法是在 Component 构造器的 options 定义段中指定 pureDataPattern 为一个正则表达式,字段名符合这个正则表达式的字段将成为纯字段。

Component({options:{pureDataPattern: /^_/  //指定所有 _ 开头的数据字段为纯数据字段},data:{a:true, //普通数据字段_b:true, //纯数据字段},methods:{myMethod(){this.data._b //纯数据字段可以在 this.data 中获取this.setData({c:true, //普通数据字段_d: true, //纯数据字段})}}
})

上述组件中的纯数据字段不会别应用到 WXML 上:

<view wx:if="{{a}}"> 这行会被展示 <view>
<view wx:if="{{_b}}"> 这行不会被展示<view>
组件属性中的纯数据字段

属性也可以被指定为纯数据字段,(遵循 pureDataPattern 的正则表达式)。
属性中的纯数据字段可以像普通属性一样接收传入的属性值,但不能将它直接用于组件自身的 WXML 中。

Component({options:{pureDataPattern: /^_/},properties:{a:Boolean,_b:{type: Boolean,_b:{type:Boolean,observer(){//这样做的话,这个observer 永远不会触发}},}}
})

注意:属性中的纯数据字段的属性 oberver 永远不会触发 , 如果想要监听属性值变化,使用 数据监听器 代替。

使用数据监听器监听纯数据字段

数据监听器 可以用于监听纯数据字段(与普通数据字段一样)。这样,可以通过监听,响应纯数据字段的变化来改变界面。
实例:将 javascript 时间戳转化为可读时间的自定义组件。

Component({options:{pureDataPattern: /^timestamp$/ //将 timestamp 属性指定为纯数据字段},properties:{timestamp: Number,},observes:{timestamp: function () {//timeStamp 被设置时,将它展示为可读时间字符串var timestring = new Date(this.data.timestamp).toLocaleString()this.setData({timeString: timeString})}}
})
<view>{{timeString}}</view>
抽象节点
在组件中使用抽象节点

有时,自定义组件模板中的一些节点,其对应的自定义组件不是由自定义组件本身确定的,而是自定义组件的调用者确定的/这时可以把这个节点声明为 “抽象节点”。
例如,我们现在来实现一个“选框组”(selecttable-group) 组件,它其中可以放置单选框或者复选框。

<!-- selecttable-group.wxml  -->
<view wx:for="{{labels}}"><label><selecttable disabled="{{false}}"></selectable>{{item}}</label>
</view>

其中,“selectable” 不是任何在 json 文件的 usingComponent 字段中声明的组件,而是一个抽象节点。它需要在 componentGenerics 字段中声明:

{"componentGenerics":{"selectable":true}
}

使用包含抽象节点的组件
在使用 selectable-group 组件时,必须指定“selectable” 具体是那个组件:

<selectable-group generic:selectable="custom-radio" />

这样,在生成这个 selected-group 组件的实例时,“selectable”节点会生成“custom-radio”组件实例。类似地,如果这样使用:

<selectable-group generic:selectable="custom-checkbox" />

“selectable”节点则会生成“custom-checkbox”组件实例。

注意:上述的 custom-radio 和 custom-checkbox 需要包含在这个 wxml 对应 json 文件的 usingComponents 定义段中。

{"usingComponents": {"custom-radio": "path/to/custom/radio","custom-checkbox": "path/to/custom/checkbox"}
}
抽象节点的默认组件

抽象节点可以指定一个默认组件,当具体组件未被指定时,将创建默认组件的实例。默认组件可以在 componentGenerics 字段中指定:

{"componentGenerics": {"selectable": {"default": "path/to/default/component"}}
}
Tips:
  • 节点的 generic 引用 generic:xxx=“yyy” 中,值 yyy 只能是静态值,不能包含数据绑定。因而抽象节点特性并不适用于动态决定节点名的场景。

微信小程序学习笔记( 自定义组件 )相关推荐

  1. 微信小程序学习笔记-自定义组件

    组定义组件方式 方式一: 局部注册 方式二: 全局注册 自定义组件位置 定义组件 <!--/components/home/home.wxml --> <view><bl ...

  2. 小程序 pagescrollto_微信小程序学习笔记(三)-- 首页及详情页开发

    一.常用组件 在上一个章节中讲解了封装请求数据的模块,在此处请求轮播图的数据 1.首页轮播图数据的请求以及渲染 1.1 轮播图数据的请求 pages/home/home.js import 2 使用组 ...

  3. 微信小程序学习笔记(1)

    微信小程序学习笔记 1.小程序代码结构 2.逻辑层和视图层 3. 小程序的宿主环境(通信模型.运行机制.组件.API) 4. 数据绑定和事件绑定 1.小程序代码结构 当开发者新建一个工程时,项目文件包 ...

  4. 自己的微信小程序学习笔记【3】——第三方UI库Lin-Ui的加载及使用

    其他微信小程序的学习笔记 自己的微信小程序学习笔记[1]--小程序开发工具的使用及项目文件说明 自己的微信小程序学习笔记[2]--从零开始新建项目 文章目录 其他微信小程序的学习笔记 前言 一.Lin ...

  5. 微信小程序学习笔记一 + 小程序介绍 前置知识

    微信小程序学习笔记一 1. 什么是小程序? 2017年度百度百科十大热词之一 微信小程序, 简称小程序, 英文名 Mini Program, 是一种不需要下载安装即可使用的应用 ( 张小龙对其的定义是 ...

  6. 微信小程序学习笔记(一)

    干货来袭 基础前言   微信⼩程序,简称⼩程序,英⽂名 Mini Program ,是⼀种不需要下载安装即可使⽤的应⽤,它实现 了应⽤"触⼿可及"的梦想,⽤⼾扫⼀扫或搜⼀下即可打开 ...

  7. 微信小程序学习笔记(七)----简单文章推荐列表和分类图标的实现

    想要实现一个顶部是几篇纯文字的推荐文章,推荐文章下面是四个分类图标,具体实现出来是这个样子的,比较简单: 首先先来找一下素材,这几个图标是我在阿里巴巴图标库下载的,这里是下载地址: http://ww ...

  8. 微信小程序学习笔记(四)自定义组件

    文章目录 1. 组件的创建与引用 1.1 创建组件 1.2 引用组件 1.3 全局引用 VS 局部引用 1.4 组件和页面的区别 2. 样式 2.1 组件样式隔离 2.2 修改组件的样式隔离选项 3. ...

  9. 微信小程序学习笔记(四)——自定义组件

    自定义组件 组件的创建与引用 创建组件 在根目录下创建 components 文件夹 右键点击 components 文件夹,选择新建 Component,就会自动生成.wxml..wxss..js. ...

  10. 微信小程序学习笔记(4)

    写在前面: 本文仅用于记录自己的学习经过和转载一些资料 本文所有练习和实例均使用vscode和微信官方的微信开发者工具共同完成,vscode仅用于编译代码.某些功能如即时模拟,实际上要使用微信官方工具 ...

最新文章

  1. 计算机模拟人工录入,用计算机模拟交互式输入代替人工进行流程录入
  2. mysql 索引- 笔记
  3. 【idea基础知识】project structure中没有web 或没有spring
  4. js文件中使用jstl或者其他标签
  5. 搜索python题目的软件_Python编程快速上手——正则表达式查找功能案例分析
  6. C++访问控制符内容相关介绍
  7. python编辑距离正则匹配_详解一道腾讯面试题:编辑距离
  8. 程序员如何保护好自己的发际线
  9. Linux系统管理——账号管理与权限及归属管理实例
  10. 苹果sf字体_字体基础知识
  11. Keras入门级MNIST手写数字识别超级详细教程
  12. 编辑中的word变成只读_打开Word文件是只读,怎么修改
  13. BZOJ3717 [PA2014] Pakowanie [状态压缩][DP]
  14. 对反游戏外挂技术的思考及实现
  15. SourceTree 对比工具配置
  16. wordpress编辑器遇到了未知错误
  17. php 解压dat,如何打开dat文件?
  18. #淘宝#复制分享宝贝内容,打开淘宝APP,自动弹出宝贝提示信息
  19. FT232RL-REEL全隔离原理图,带I/O保护,防电流倒灌等
  20. qt项目移植报的错误和解决方法

热门文章

  1. vmware虚拟机无法上网的几点解决方案
  2. 数据结构习题--栈与队列(1)
  3. 操作系统存储管理练习题
  4. 获得到当前月底时间(秒)
  5. 互联网产品定位深层次分析
  6. Flutter路由管理和页面参数的传递(源码分析)
  7. 一句话概括Comparator接口实现原理
  8. Could not build wheels for tokenizers, which is required to install pyproject.toml-based projects
  9. HTML学习之链接target属性
  10. 12.05—12.11java学习周记