标题 | 详情 |
---|---|
作者简介 | 愚公搬代码 |
头衔 | 华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。 |
近期荣誉 | 2022年度博客之星TOP2,2023年度博客之星TOP2,2022年华为云十佳博主,2023年华为云十佳博主等。 |
博客内容 | .NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。 |
欢迎 | 👍点赞、✍评论、⭐收藏 |
在微信小程序的开发中,自定义组件是实现灵活、可复用代码的重要工具。随着小程序生态的不断发展,开发者对于组件的需求也日益增长,从基础的组件使用到高级的技巧与模式,掌握自定义组件的高级用法已成为提升开发效率和用户体验的关键所在。
本文将深入探讨微信小程序中自定义组件的高级用法,包括组件之间的通信、生命周期管理、插槽机制、样式封装等内容。我们将通过实际案例和最佳实践,帮助您更好地理解和应用这些高级功能,从而为您的项目带来更高的灵活性和可维护性。
无论您是希望优化已有小程序的开发者,还是渴望学习新技术的爱好者,相信本文都将为您提供丰富的知识和实用的技巧,让您在小程序开发的道路上走得更远、更稳。
让我们一起深入探索,让自定义组件的高级用法为您的开发之旅增添无限可能!
目前我们已经对自定义组件的使用有了大致的体验,其实关于自定义组件,还有许多高级的特性供开发者进行使用,本节将向大家介绍这些特性。
WXML
文件用于定义组件的模板,描述组件内部的结构。<slot>
标签),插槽类似于接口,允许组件的使用方向组件中传入内容。WXSS
文件用于定义组件的样式。组件内的样式只会作用于组件内部的元素,而不会影响外部页面或其他组件。WXSS
样式时,需要注意以下几点:插槽是一种允许外部内容插入组件的机制。组件可以通过插槽定义一个或多个内容区,从而使得组件更加灵活。
<slot>
标签所在的位置。示例:
<!-- component1.wxml -->
<view>
<view style="text-align: center;">{{title}}</view>
<slot></slot> <!-- 默认插槽 -->
</view>
<!-- 使用组件时 -->
<component1 title="按钮">
<button type="primary" size="mini">按钮1</button>
</component1>
在这个示例中,<button>
标签会被插入到 <slot></slot>
标签的位置。
options: { multipleSlots: true }
来启用多插槽功能。name
属性进行命名,以区分不同插槽的位置。示例:
<!-- component1.wxml -->
<view>
<view style="text-align: center;">{{title}}</view>
<slot></slot> <!-- 默认插槽 -->
<view style="display: flex; flex-direction: row; justify-content: space-between;">
<slot name="left"></slot> <!-- 左侧插槽 -->
<slot name="right"></slot> <!-- 右侧插槽 -->
</view>
</view>
<!-- 使用组件时 -->
<component1 title="按钮组">
<button type="primary" size="mini">按钮1</button>
<button type="primary" size="mini">按钮2</button>
<button type="primary" size="mini">按钮3</button>
<view slot="left">左视图</view> <!-- 左插槽 -->
<view slot="right">右视图</view> <!-- 右插槽 -->
</component1>
在这个例子中,<view slot="left">
会插入到 <slot name="left">
插槽中,<view slot="right">
会插入到 <slot name="right">
插槽中,而没有指定 slot
的内容会插入到默认插槽中。
为了避免组件的样式影响到页面的其他部分,或页面样式影响到组件,可以通过配置 styleIsolation
来进行样式隔离。
styleIsolation
选项的可选值:
isolated
:启用样式隔离,组件内部的样式与外部页面样式不会互相影响。apply-shared
:页面的样式会影响到组件内部,但组件内的样式不会影响页面。shared
:组件与页面共享样式,组件内部的样式与外部页面样式可以相互影响。示例:
// component1.js
Component({
options: {
styleIsolation: 'isolated' // 启用样式隔离
}
});
为了让组件的样式更加灵活,组件可以通过 externalClasses
选项接受外部样式。使用外部类名可以让外部的样式覆盖组件的默认样式。
示例:
// component1.js
Component({
externalClasses: ['title-class'] // 允许外部传入 'title-class' 样式
});
<!-- component1.wxml -->
<view>
<view style="text-align: center;">
<text class="title-class">{{title}}</text> <!-- 使用外部传入的样式 -->
</view>
<slot></slot>
<view style="display: flex; flex-direction: row; justify-content: space-between;">
<slot name="left"></slot>
<slot name="right"></slot>
</view>
</view>
在使用组件时,可以通过传入自定义的样式来覆盖组件的默认样式:
<!-- 使用组件时 -->
<component1 title="按钮组" title-class="title"></component1>
<!-- 在 customComponent.wxss 中 -->
.title {
color: red; /* 修改标题颜色为红色 */
}
WXML
用于定义组件的结构,支持插槽(默认插槽和具名插槽)。WXSS
用于定义组件的样式,推荐使用类选择器并避免使用 ID、属性选择器等。name
属性的 <slot>
标签。name
属性区分多个插槽。styleIsolation
配置样式隔离模式。externalClasses
让组件能够接收外部传入的样式。通过这些机制,组件的设计可以更加灵活和模块化,方便开发者进行复用和定制化。
在小程序中,组件间的通信主要分为以下几种情况:
我们将重点讨论第2种和第3种应用场景。
父组件向子组件传递数据通常通过 properties
外部属性进行实现。父组件在使用子组件时,通过设置子组件的 properties
属性来传递数据。这种方式是相对简单和直接的,且在上文已经有过讨论,这里不再重复。
子组件向父组件传递数据一般通过 自定义事件 来实现。子组件通过 triggerEvent
方法触发自定义事件,将需要传递的数据作为参数传递给父组件。
WXML
文件中绑定事件:<!-- component1.wxml -->
<text class="title-class" bindtap="tapTitle">{{title}}</text>
JS
文件中定义触发事件的方法:// component1.js
Component({
properties: {
title: {
type: String,
value: '默认标题'
}
},
methods: {
tapTitle: function() {
// 触发自定义事件,将数据传递给父组件
this.triggerEvent('titleTapEvent', {
title: this.properties.title
});
}
}
});
<!-- pages/customComponent/customComponent.wxml -->
<component1 title="按钮组" bindtitleTapEvent="tapEvent" title-class="title"></component1>
JS
文件中监听子组件的事件:// customComponent.js
Page({
tapEvent: function(event) {
console.log(event.detail.title); // 输出:按钮组
}
});
在这个例子中,父组件通过 bindtitleTapEvent="tapEvent"
来监听子组件的 titleTapEvent
事件,子组件通过 this.triggerEvent
将数据传递给父组件,父组件可以在回调中获取事件的数据。
triggerEve
nt 的第三个参数triggerEvent
方法有一个可选的第三个参数,可以设置事件的一些额外选项,包括事件是否冒泡、是否可以穿越组件边界等。第三个参数的选项包括:
属性名 | 类型 | 说明 |
---|---|---|
| 布尔值 | 事件是否冒泡 |
| 布尔值 | 事件是否拥有捕获阶段 |
| 布尔值 | 事件是否可以穿越组件边界(默认为 |
示例:
this.triggerEvent('titleTapEvent', { title: this.properties.title }, { bubbles: true, composed: true });
在某些情况下,父组件可能需要获取子组件的实例,以便直接调用子组件中的方法或访问子组件的数据。这可以通过 selectComponent
方法来实现。
WXML
文件中为子组件添加类名:<!-- pages/customComponent/customComponent.wxml -->
<component1 class="my-component" title="按钮组" bindtitleTapEvent="tapEvent" title-class="title"></component1>
JS
文件中通过 selectComponent
获取子组件实例:// customComponent.js
Page({
onShow: function() {
// 获取子组件实例
let component = this.selectComponent('.my-component');
// 获取子组件中的数据
console.log(component.properties.title); // 输出:按钮组
// 调用子组件的方法
component.tapTitle(); // 调用子组件的 tapTitle 方法
}
});
通过 selectComponent
,父组件可以访问到子组件的实例,从而获取其数据或调用其方法。
component-expo
rt Behavior 导出组件方法有时开发者不希望让外部直接访问子组件的内部数据和方法,这时候可以使用 component-export
Behavior 来导出子组件的部分数据和方法。
component-export
:// component1.js
Component({
behaviors: ['wx://component-export'],
properties: {
title: { type: String, value: '默认标题' }
},
methods: {
tapTitle: function() {
this.triggerEvent('titleTapEvent', { title: this.properties.title });
}
},
export: function() {
return {
outData: '暴露到外部的数据',
outFunction: function() {
console.log('暴露给外部的方法');
}
};
}
});
// customComponent.js
Page({
onShow: function() {
// 获取子组件实例
let component = this.selectComponent('.my-component');
// 获取导出的数据和方法
console.log(component.outData); // 输出:暴露到外部的数据
component.outFunction(); // 输出:暴露给外部的方法
}
});
通过 component-export
,父组件只能访问到子组件明确暴露的数据和方法,避免了直接访问子组件的内部实现,从而增强了组件的封装性。
properties
外部属性。triggerEvent
触发事件,将数据传递给父组件。selectComponent
获取子组件实例,访问其数据或调用其方法。component-export
Behavior 来按需暴露子组件的方法和数据,保护内部实现。通过这些通信方式,小程序中的组件化开发变得更加灵活,能够有效地管理父子组件之间的交互,提升应用的可维护性和扩展性。
在小程序中,复杂的自定义组件可能由多个子组件构成,且这些子组件之间可能存在依赖关系。例如,想要开发一个列表组件,它至少需要两个子组件:一个是列表框架(customList
),另一个是列表项(customItem
)。在这种情况下,customItem
必须是 customList
的子组件,这就创建了组件之间的依赖关系。
假设你要开发一个自定义列表组件(customList
)和列表项组件(customItem
),项目结构如下:
components/
└── component2/
├── customList/
│ ├── customList.wxml
│ ├── customList.js
│ ├── customList.json
│ └── customList.wxss
└── customItem/
├── customItem.wxml
├── customItem.js
├── customItem.json
└── customItem.wxss
通过配置 relations
来定义组件之间的关系。在你提到的案例中,customItem
是 customList
的子组件,可以通过 relations
选项进行关联。
customLi
st 组件中定义关系在 customList.wxml
中定义组件结构,使用 <slot></slot>
插入子组件内容:
<!-- component/component2/customList.wxml -->
<view>
<view>header</view>
<slot></slot>
</view>
在 customList.js
中,使用 relations
来设置与 customItem
的关系:
// component/component2/customList.js
Component({
relations: {
'./customItem': {
type: 'child', // 指定 customItem 为 child 组件
linked: function(target) {
console.log('Item组件被插入');
},
linkChanged: function(target) {
console.log('Item组件发生变化');
},
unlinked: function(target) {
console.log('Item组件被移除');
}
}
}
});
type: 'child'
表示 customItem
是 customList
的子组件。linked
、linkChanged
和 unlinked
是回调函数,分别在子组件插入、位置变化和移除时触发。在上述代码中,父组件 customList
与子组件 customItem
之间的关系被通过 relations
来定义。当 customItem
被插入到 customList
时,linked
回调会触发;当子组件移除时,unlinked
回调会触发;当子组件在 DOM 树中位置发生变化时,linkChanged
会触发。
除了父子关系,还可以定义组件的祖先(ancestor
)和后代(descendant
)关系。在你的示例中,还提到了列表头部(customHeader
)和尾部(customFooter
)与列表容器(customList
)的关系。
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
relations: {
'./customList': {
type: 'ancestor', // customList 为祖先节点
linked: function(target) {
console.log('Header 插入');
}
}
}
});
// component/component2/customFooter.js
var custom = require('./custom');
Component({
behaviors: [custom],
relations: {
'./customList': {
type: 'ancestor', // customList 为祖先节点
linked: function(target) {
console.log('Footer 插入');
}
}
}
});
Behavior
进行多组件关联通过 Behavior
可以实现一对多的组件关系定义,简化多个子组件的关系管理。你可以定义一个空的 Behavior
来将多个组件与 customList
关联。
Behavi
or在 custom.js
中定义一个空的 Behavior
:
// component/component2/custom.js
module.exports = Behavior({});
然后在 customHeader
和 customFooter
组件中引入该 Behavior
:
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
relations: {
'./customList': {
type: 'ancestor',
linked: function(target) {
console.log('Header 插入');
}
}
}
});
// component/component2/customFooter.js
var custom = require('./custom');
Component({
behaviors: [custom],
relations: {
'./customList': {
type: 'ancestor',
linked: function(target) {
console.log('Footer 插入');
}
}
}
});
Behavior
进行目标节点管理在 customList
组件中,你可以通过 target
选项,利用 Behavior
来管理所有拥有该 Behavior
的节点。
// component/component2/customList.js
var custom = require('./custom');
Component({
relations: {
'custom': {
type: 'descendant',
target: custom // 目标为 custom Behavior
}
}
});
这样,所有拥有 custom
Behavior
的组件(如 customHeader
和 customFooter
)都会被自动关联到 customList
组件。
relations
来定义组件之间的依赖关系。可以定义父子关系(child
)、祖先后代关系(ancestor
和 descendant
)。linked
、linkChanged
和 unlinked
回调函数,父组件可以感知子组件的插入、更新和移除。Behavior
,可以将一类组件的共同行为集中管理,简化多组件间的关系定义。target
选项,可以将 Behavior
作为目标节点,自动关联所有包含该 Behavior
的组件。Behavior
的作用Behavior
是一种提高代码复用性的编程方式。通过 Behavior
,可以将多个组件中重复的功能提取成一个独立的模块,然后通过引入 Behavior
来实现功能的复用。这种方法可以让多个自定义组件共享相同的属性、数据、方法和生命周期回调,避免重复代码,提高开发效率。
Behavior
实现代码复用假设你有两个组件,customHeader
和 customFooter
,它们有一些共同的功能。你可以将这些功能提取到 Behavior
中,并让这两个组件都引入它,从而复用这些功能。
Behavi
or首先,你需要在 custom.js
文件中定义一个 Behavior
:
// component/component2/custom.js
module.exports = Behavior({
properties: {
"title": {
type: String
}
},
data: {
behaviorData: "behaviorData"
},
methods: {
log: function(value) {
console.log("自定义的打印方法:" + value);
}
},
created: function() {
console.log("Behavior created");
}
});
这里定义了:
Behavior
的外部属性,组件引入该 Behavior
后,就可以使用 title
属性。Behavior
内部的数据,behaviorData
。Behavior
中的方法,log
方法可以被组件调用。created
),在 Behavior
创建时会调用。 Behavi
or然后在 customHeader
和 customFooter
组件中引入这个 Behavior
:
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
relations: {
'./customList': {
type: 'ancestor',
linked: function(target) {
console.log('Header 插入');
}
}
}
});
// component/component2/customFooter.js
var custom = require('./custom');
Component({
behaviors: [custom],
relations: {
'./customList': {
type: 'ancestor',
linked: function(target) {
console.log('Footer 插入');
}
}
}
});
WX
ML 中使用 Behavi
or 属性组件 customHeader
和 customFooter
都引入了 custom
Behavior
,因此它们都能够使用 Behavior
中定义的 title
属性和 log
方法。在组件的 WXML
文件中,我们可以直接使用 title
属性:
<!-- component/component2/customHeader.wxml -->
<text>{{title}}</text>
<!-- component/component2/customFooter.wxml -->
<text>{{title}}</text>
在页面中使用这两个组件时,直接为它们传递 title
属性即可:
<!-- pages/customComponent/customComponent.wxml -->
<custom-list>
<custom-header title="头部"></custom-header>
<custom-footer title="尾部"></custom-footer>
</custom-list>
当页面渲染时,customHeader
和 customFooter
组件都会正确显示传入的 title
属性。
Behavior
的属性与覆盖规则 Behavi
or 对象的属性Behavior
中可以定义的属性有以下几种:
属性 | 意义 | 对应组件中的功能 |
---|---|---|
| 组件的外部属性,类似于组件的 | 通过 |
| 组件的内部数据,类似于组件的 | 通过 |
| 组件的方法,类似于组件的 | 通过 |
| 引入其他 | 允许一个 |
|
| 在 |
|
| 在 |
|
| 在 |
|
| 在 |
当组件同时定义了自己的属性、数据、方法与生命周期回调,同时又引入了 Behavior
,会有以下覆盖和组合规则:
properties
:如果组件和 Behavior
中都定义了相同的属性,组件内部定义的属性优先级更高。如果组件中没有定义该属性,则会从 Behavior
中继承。methods
:如果组件和 Behavior
中都定义了相同的方法,组件内部的方法优先级更高。如果组件没有定义该方法,则会从 Behavior
中继承。data
:如果组件和 Behavior
中都有同名的数据,并且数据的类型为对象,则会合并数据;否则,组件中的数据优先级更高。Behavior
中的生命周期方法不会互相覆盖,会依次执行。先执行父 Behavior
的生命周期方法,再执行子 Behavior
的方法,最后执行组件本身的生命周期方法。后引入的 Behavior
会优先于前一个 Behavior
执行。Behavior
小程序框架还提供了一些内置的 Behavior
,可以直接在自定义组件中使用,常见的内置 Behavior
有:
Behavior 名 | 意义 |
---|---|
| 为自定义组件增加表单控制能力,使 |
| 为自定义组件增加表单控件组,使 |
| 为自定义组件增加表单按钮控件支持,使 |
| 使自定义组件支持 |
这些内置 Behavior
可以帮助你在自定义组件中实现常见的功能,比如表单字段控制、按钮控制等,避免重复造轮子。
Behavior
允许将组件中共享的功能提取到一个独立模块,通过引入 Behavior
,可以在多个组件中复用相同的属性、数据、方法和生命周期回调。Behavior
的关系:组件可以通过 behaviors
配置引入一个或多个 Behavior
,并继承其中定义的功能。组件可以覆盖 Behavior
中的同名字段,或者与其合并。Behavior
:小程序提供了内置 Behavior
,如表单控制等,可以直接引入,提高开发效率。Behavior
特别适用于多个组件具有相同功能或行为的场景,避免重复定义,提高代码的可维护性和复用性。🔎5.数据监听器小程序中的组件可以使用 数据监听器 来监控属性或数据字段的变化,当这些数据发生变化时,数据监听器会触发指定的回调函数,从而执行一些逻辑操作。
数据监听器通过 observers
选项定义,支持对多个数据字段进行监听。当任一字段发生变化时,监听函数都会被触发。
假设有一个组件 customHeader
,它引入了 Behavior
,并拥有 title
属性和 behaviorData
数据字段。你可以在 observers
选项中设置监听器,监听这些字段的变化。
示例:监听 title
和 behaviorData
的变化
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
observers: {
"title,behaviorData": function(title, behaviorData) {
console.log("title 或 behaviorData 被设置", title, behaviorData);
}
}
});
在这个示例中,当 title
或 behaviorData
发生变化时,监听器函数会被调用,console.log
会输出变化后的 title
和 behaviorData
。
如果你想监听一个对象内部某个属性的变化,可以像下面这样使用 observers
:
示例:监听对象属性 obj.name
的变化
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
data: {
obj: {
name: "name",
id: "1"
}
},
attached: function() {
// 修改 obj 的数据
this.setData({
obj: {
name: "huishao",
id: "1"
}
});
},
observers: {
"obj.name": function(name) {
console.log("obj.name 被设置为", name);
}
}
});
在这个例子中,监听器会监控 obj.name
的变化,当 obj.name
更新时,回调函数会被触发,并打印出新的 name
值。
如果需要监听对象中所有属性的变化,可以使用通配符 **
来实现。例如,如果你想监听整个 obj
对象的变化,可以这样做:
示例:监听整个对象 obj
的变化
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
data: {
obj: {
name: "name",
id: "1"
}
},
observers: {
"obj.**": function(obj) {
console.log("obj 中的任意字段发生变化", obj);
}
}
});
使用 "obj.**"
作为监听字段,意味着你可以监听 obj
对象内任意属性的变化。当 obj
内的任何字段发生变化时,监听器都会被触发,打印出变化后的 obj
数据。
setData
设置的数据字段:
数据监听器仅能监听通过 setData
方法更新的数据字段。如果你直接修改数据对象的值,而没有使用 setData
,那么数据监听器是不会被触发的。setData
设置相同的字段可能会导致无限循环。例如,如果你在监听器中修改了 title
或 behaviorData
的值,且修改的数据依然与之前相同,那么 setData
会触发监听器再次执行,形成无限循环。因此,应该避免在监听器内直接修改监听字段,除非你有办法判断是否需要更新。示例:避免无限循环
observers: {
"title": function(title) {
console.log("title 被设置为", title);
// 如果 title 没有变化,避免再次调用 setData
if (title !== this.data.title) {
this.setData({
title: title
});
}
}
}
在上面的代码中,首先通过条件判断 title
是否发生变化,只有在 title
确实发生了变化时,才调用 setData
。这样可以避免因 setData
调用导致的无限循环。
observers
对多个字段进行合并监听,多个字段的变化都会触发同一个监听函数。**
通配符监听对象的所有属性变化。setData
更新的数据字段,且避免在监听器中修改同一数据字段,否则可能会引起无限循环。这样,你就可以灵活地监控和处理组件内的数据变化,在小程序开发中实现动态更新和响应。
在小程序中,组件中的数据通常会参与页面渲染,但有时候并不需要所有的数据都渲染到页面上。为了优化性能,你可以将某些 不需要渲染到页面的字段 定义为纯数据字段。这些字段依然会存在于 data
中,但不会参与页面的渲染。
纯数据字段的作用
要将某些数据字段设置为纯数据字段,你需要在组件的 options
配置中设置 pureDataPattern
字段,并通过正则表达式规则来指定哪些字段是纯数据字段。所有符合该规则的字段都会被视为纯数据字段。
步骤:配置 pureDataPattern
options
:你可以在组件的 options
选项中设置 pureDataPattern
,它是一个正则表达式,用于定义哪些字段是纯数据字段。data
中定义的字段,只要符合正则表达式规则,就会被自动解析为纯数据字段。示例:设置纯数据字段
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
options: {
// 通过正则表达式,定义所有以下划线开头的字段作为纯数据字段
pureDataPattern: /^_/
},
data: {
data1: "渲染字段", // 该字段会参与页面渲染
_data2: "纯数据字段" // 该字段是纯数据字段,不会参与页面渲染
}
});
在这个例子中,pureDataPattern
被设置为 ^_
,这意味着所有以 _
开头的字段会被视为纯数据字段。因此,_data2
被当作纯数据字段,而 data1
仍然是正常的数据字段,会参与页面渲染。
WXML
中使用了 _data2
,不会有任何渲染效果,因为它是纯数据字段,不参与页面的渲染。示例:监听纯数据字段的变化
observers: {
"_data2": function(newData) {
console.log("纯数据字段 _data2 发生变化", newData);
// 你可以在此根据 _data2 的变化更新页面需要渲染的字段
this.setData({
data1: "新渲染字段"
});
}
}
在上面的代码中,当 _data2
变化时,监听器会被触发。你可以在监听器中执行一些操作,比如更新其他数据字段,进而影响页面的渲染。
通过合理地使用纯数据字段,可以有效减少不必要的数据变化对页面渲染的影响,特别是在数据量较大的情况下。这能显著提升页面的性能,尤其是在频繁更新数据的场景中,避免不必要的渲染和性能消耗。
data
中,但不直接参与页面渲染的数据字段。它们的变化可以被监听,但不会影响页面渲染。pureDataPattern
:通过在 options
配置中设置 pureDataPattern
字段,使用正则表达式来指定哪些字段是纯数据字段。示例总结
// component/component2/customHeader.js
var custom = require('./custom');
Component({
behaviors: [custom],
options: {
pureDataPattern: /^_/ // 所有以 "_" 开头的字段都为纯数据字段
},
data: {
data1: "渲染字段", // 正常数据字段,会参与页面渲染
_data2: "纯数据字段" // 纯数据字段,不参与页面渲染
},
observers: {
"_data2": function(newData) {
console.log("纯数据字段 _data2 发生变化", newData);
// 可以通过监听纯数据字段的变化来更新渲染字段
this.setData({
data1: "更新后的渲染字段"
});
}
}
});
通过这种方式,你可以优化小程序的性能,尤其是在处理复杂数据时,可以减少不必要的渲染操作,提高页面响应速度。
抽象节点并不代表一个具体的组件类型,它更多的是一种占位符,允许自定义组件的调用者决定如何渲染某些节点内容。在自定义组件中,有时候某些节点不由组件本身决定,而是交给调用者来定义,这时就可以使用抽象节点。
要使用抽象节点,首先需要在组件的 JSON 配置文件 中进行配置。具体来说,在组件的 customList.json
文件中,通过配置 componentGenerics
来定义抽象节点。
示例:配置抽象节点
假设我们有一个自定义组件 customList
,其中我们希望标题部分交给调用者定义。我们将这个标题部分定义为一个抽象节点 outtitle
。
// customList.json
{
"component": true,
"componentGenerics": {
"outtitle": true
}
}
在这个配置中,componentGenerics
用于定义组件中的抽象节点。这里我们配置了一个名为 outtitle
的抽象节点,调用者可以决定这个节点的具体渲染内容。
一旦抽象节点在配置文件中定义,就可以在组件的模板文件中(例如 customList.wxml
)使用它。
在 customList.wxml
中使用抽象节点
<!-- customList.wxml -->
<view>
<outtitle title="标题"></outtitle>
<slot></slot>
</view>
在上面的模板中,<outtitle>
是我们定义的抽象节点,调用者可以提供自己的组件来替代这个节点,从而自定义标题部分的内容。<slot>
是插槽,用于插入调用者传递的内容。
调用 customList
组件时,调用者可以指定一个具体的组件来替代抽象节点。假设我们有一个 component1
组件,支持 title
属性,我们可以将 component1
作为 outtitle
节点的内容。
示例:在调用方的 WXML 文件中传递组件
<!-- pages/customComponent/customComponent.wxml -->
<custom-list generic:outtitle="component1"></custom-list>
在这个例子中,我们通过 generic:outtitle="component1"
将 component1
组件绑定到 customList
组件中的 outtitle
抽象节点。这样,在渲染时,outtitle
会被替换成 component1
组件。
由于我们无法保证调用方每次都按照预期提供组件,组件开发者可以为抽象节点设置一个默认的组件。这样,如果调用者没有提供自定义的组件,组件会使用默认的组件。
示例:为抽象节点设置默认组件
// customList.json
{
"component": true,
"usingComponents": {},
"componentGenerics": {
"outtitle": {
"default": "../component1/component1"
}
}
}
在这个配置中,outtitle
的默认组件设置为 ../component1/component1
。如果调用者没有传递具体的组件,customList
会默认使用 component1
组件来渲染 outtitle
。
customList
的配置:// customList.json
{
"component": true,
"usingComponents": {},
"componentGenerics": {
"outtitle": {
"default": "../component1/component1"
}
}
}
customList
的模板:<!-- customList.wxml -->
<view>
<outtitle title="标题"></outtitle>
<slot></slot>
</view>
customList
组件时,指定具体的 outtitle
组件:<!-- pages/customComponent/customComponent.wxml -->
<custom-list generic:outtitle="component1"></custom-list>
outtitle
**,则默认使用** component1
组件:<!-- 如果调用时没有指定 generic:outtitle,则会默认渲染 component1 -->
<custom-list></custom-list>
componentGenerics
配置抽象节点,并通过正则或指定节点名称来定义。generic
属性传递具体的组件,用来替换抽象节点。抽象节点的使用能够提升自定义组件的灵活性和可复用性,让组件开发者不需要关心某些具体细节,而是交给调用者来定制。
setData
时,小程序会触发一次页面更新,而这种更新操作会产生一定的性能开销。setData
调用,避免多次触发页面刷新。setData
,避免无意义的刷新操作。setUpdatePerformanceListener
方法为了更好地了解每次 setData
刷新操作的性能开销,小程序提供了 setUpdatePerformanceListener
方法,允许开发者设置 更新性能回调。通过这个回调,我们可以实时获取页面刷新时的性能数据,帮助我们分析和优化性能。
在组件的 attached
生命周期方法中,可以调用 setUpdatePerformanceListener
来监听页面更新性能。
// customHeader.js
Component({
attached: function() {
this.setUpdatePerformanceListener(
{ withDataPaths: true }, // 配置回调时是否传递引起页面刷新的数据字段
(res) => {
console.log(res); // 打印更新的性能数据
}
);
// 设置组件数据,触发页面更新
this.setData({
obj: { name: "huishao", id: "1" }
});
}
});
setUpdatePerformanceListener
方法会触发一个回调,回调函数的参数包含了具体的性能数据。以下是回调函数参数中的常见属性及其意义:
属性名 | 类型 | 说明 |
---|---|---|
| 数值 | 当前更新过程的唯一标识,标记此次更新的不同阶段 |
| 数值 | 如果是子更新,则返回父更新的 |
| 布尔值 | 是否是合并更新(即多次数据更新合并为一次刷新) |
| 数组 | 引起此次更新的数据字段路径 |
| 数值 | 更新进入等待队列时的时间戳 |
| 数值 | 更新开始运算的时间戳 |
| 数值 | 更新运算结束的时间戳 |
isMergedUpdate
:当为 true
时,表示这次更新是合并更新,即多个字段的变更被合并成一次刷新操作,这样可以减少刷新频次,提升性能。dataPaths
:该属性记录了具体引起更新的数据字段路径。如果我们在一次更新中变更了多个字段,dataPaths
会列出所有字段的路径。pendingStartTimestamp
**, updateStartTimestamp
**,** updateEndTimestamp
**)**:这些时间戳可以帮助开发者计算更新的耗时,从更新请求发出到更新结束所消耗的时间。通过收集性能数据,开发者可以更好地分析每次刷新操作的开销,并根据数据进行优化。以下是一些具体的优化建议:
isMergedUpdate
属性,检查是否多个更新合并成了一次更新。如果没有合并,可以考虑手动合并 setData
调用,减少刷新次数。setData
调用:通过 dataPaths
了解哪些字段导致了更新。若是无关紧要的字段变化,避免将这些字段纳入更新中。updateStartTimestamp
和 updateEndTimestamp
的差值,可以计算每次更新操作的耗时,进一步优化长时间更新的部分。pendingStartTimestamp
的时间较长,可以优化数据更新的时机,减少页面更新进入等待队列的时间。setData
进行数据更新,合并多个变更操作,避免频繁的页面刷新是优化的关键。setUpdatePerformanceListener
:通过这个方法,可以获取到页面刷新过程中的详细性能数据,包括是否合并更新、更新字段、时间戳等,从而帮助开发者分析性能瓶颈。isMergedUpdate
属性,避免多次单独更新。dataPaths
和时间戳信息,分析并减少不必要的更新开销。setData
调用、减少不必要的刷新、控制更新时机等手段,可以显著提高页面性能,提升用户体验。示例代码总结
Component({
attached: function() {
this.setUpdatePerformanceListener(
{ withDataPaths: true }, // 配置是否传递字段路径
(res) => {
console.log(res); // 输出性能数据
// 根据性能数据进行分析和优化
}
);
// 设置组件数据,触发页面更新
this.setData({
obj: { name: "huishao", id: "1" }
});
}
});
通过使用性能回调,开发者能够实时掌握页面更新的详细信息,并依据这些信息进行性能优化。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。