444 lines
9.1 KiB
Markdown
444 lines
9.1 KiB
Markdown
# 自定义组件
|
||
|
||
> 官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/
|
||
|
||
基础库 1.6.3+ 支持。
|
||
|
||
## 创建自定义组件
|
||
|
||
一个自定义组件由 `json` `wxml` `wxss` `js` 四个文件组成。
|
||
|
||
```json
|
||
// my-component.json — 声明为组件
|
||
{ "component": true }
|
||
```
|
||
|
||
```xml
|
||
<!-- my-component.wxml -->
|
||
<view class="inner">
|
||
{{innerText}}
|
||
<slot></slot>
|
||
</view>
|
||
```
|
||
|
||
```css
|
||
/* my-component.wxss — 样式只作用于本组件 */
|
||
.inner { color: red; }
|
||
```
|
||
|
||
```javascript
|
||
// my-component.js
|
||
Component({
|
||
properties: {
|
||
innerText: {
|
||
type: String,
|
||
value: 'default value'
|
||
}
|
||
},
|
||
data: {
|
||
someData: {}
|
||
},
|
||
methods: {
|
||
customMethod() {}
|
||
}
|
||
})
|
||
```
|
||
|
||
### 使用自定义组件
|
||
|
||
```json
|
||
// 页面或组件的 .json
|
||
{
|
||
"usingComponents": {
|
||
"my-component": "/components/my-component/my-component"
|
||
}
|
||
}
|
||
```
|
||
|
||
```xml
|
||
<!-- 页面 wxml -->
|
||
<my-component inner-text="Some text">
|
||
<view>这里是插入到 slot 中的内容</view>
|
||
</my-component>
|
||
```
|
||
|
||
**注意事项**:
|
||
- 标签名只能是小写字母、中划线、下划线的组合
|
||
- 组件和页面所在项目根目录名不能以 "wx-" 为前缀
|
||
- 使用 `usingComponents` 会使页面的 `this` 原型稍有差异(多了 `selectComponent` 等方法)
|
||
- 使用 `usingComponents` 时,`setData` 内容不会被深复制(性能优化)
|
||
|
||
## Component() 构造器
|
||
|
||
```javascript
|
||
Component({
|
||
// ===== 组件属性(外部传入) =====
|
||
properties: {
|
||
myProperty: {
|
||
type: String, // 类型:String, Number, Boolean, Object, Array, null(任意)
|
||
value: '', // 默认值
|
||
observer(newVal, oldVal) {
|
||
// 属性变化时触发(已不推荐,建议用 observers)
|
||
}
|
||
},
|
||
myProperty2: String // 简化定义
|
||
},
|
||
|
||
// ===== 组件内部数据 =====
|
||
data: {
|
||
someData: 'initial'
|
||
},
|
||
|
||
// ===== 生命周期(推荐写在 lifetimes 中) =====
|
||
lifetimes: {
|
||
created() {
|
||
// 组件实例刚被创建时
|
||
// 此时不能调用 setData,通常用于给 this 添加自定义属性
|
||
},
|
||
attached() {
|
||
// 组件实例进入页面节点树时
|
||
// 大多数初始化工作在此进行
|
||
},
|
||
ready() {
|
||
// 组件在视图层布局完成后
|
||
},
|
||
moved() {
|
||
// 组件实例被移动到节点树另一个位置时
|
||
},
|
||
detached() {
|
||
// 组件实例被从页面节点树移除时
|
||
// 清理工作(如清除定时器)
|
||
},
|
||
error(err) {
|
||
// 组件方法抛出错误时(基础库 2.4.1+)
|
||
}
|
||
},
|
||
|
||
// ===== 组件所在页面的生命周期 =====
|
||
pageLifetimes: {
|
||
show() {
|
||
// 页面被展示时
|
||
},
|
||
hide() {
|
||
// 页面被隐藏时
|
||
},
|
||
resize(size) {
|
||
// 页面尺寸变化时
|
||
},
|
||
routeDone() {
|
||
// 页面路由动画完成时(基础库 2.31.2+)
|
||
}
|
||
},
|
||
|
||
// ===== 数据监听器(基础库 2.6.1+) =====
|
||
observers: {
|
||
'numberA, numberB'(numberA, numberB) {
|
||
// numberA 或 numberB 变化时触发
|
||
this.setData({ sum: numberA + numberB })
|
||
},
|
||
'some.subfield'(subfield) {
|
||
// 监听子数据字段
|
||
},
|
||
'arr[12]'(val) {
|
||
// 监听数组某一项
|
||
},
|
||
'some.field.**'(field) {
|
||
// 使用通配符监听所有子数据字段
|
||
},
|
||
'**'() {
|
||
// 监听所有 setData(每次 setData 都触发,慎用)
|
||
}
|
||
},
|
||
|
||
// ===== 方法 =====
|
||
methods: {
|
||
onMyButtonTap() {
|
||
this.setData({ someData: 'new value' })
|
||
},
|
||
// 内部方法建议以下划线开头
|
||
_myPrivateMethod() {
|
||
this.setData({ 'A.B': 'myPrivateData' })
|
||
}
|
||
},
|
||
|
||
// ===== behaviors =====
|
||
behaviors: [],
|
||
|
||
// ===== 其他选项 =====
|
||
options: {
|
||
multipleSlots: true, // 启用多 slot(默认只能一个)
|
||
styleIsolation: 'isolated', // 样式隔离模式
|
||
pureDataPattern: /^_/, // 纯数据字段正则
|
||
virtualHost: true // 虚拟化组件节点(基础库 2.11.2+)
|
||
},
|
||
|
||
// ===== 外部样式类 =====
|
||
externalClasses: ['my-class'],
|
||
|
||
// ===== 组件间关系 =====
|
||
relations: {},
|
||
|
||
// ===== 导出(配合 wx://component-export) =====
|
||
export() {
|
||
return { myField: 'myValue' }
|
||
}
|
||
})
|
||
```
|
||
|
||
## 组件模板和样式
|
||
|
||
### 多 slot
|
||
|
||
```javascript
|
||
// 组件 js
|
||
Component({
|
||
options: { multipleSlots: true }
|
||
})
|
||
```
|
||
|
||
```xml
|
||
<!-- 组件 wxml -->
|
||
<view>
|
||
<slot name="before"></slot>
|
||
<view>组件内部内容</view>
|
||
<slot name="after"></slot>
|
||
</view>
|
||
|
||
<!-- 使用 -->
|
||
<my-component>
|
||
<view slot="before">before 内容</view>
|
||
<view slot="after">after 内容</view>
|
||
</my-component>
|
||
```
|
||
|
||
### 样式隔离 styleIsolation
|
||
|
||
```javascript
|
||
Component({
|
||
options: {
|
||
styleIsolation: 'isolated'
|
||
// 'isolated'(默认):组件样式完全隔离
|
||
// 'apply-shared':页面 wxss 样式会影响组件,但组件不影响页面
|
||
// 'shared':页面和组件样式互相影响
|
||
}
|
||
})
|
||
```
|
||
|
||
也可在 json 中配置:
|
||
```json
|
||
{ "styleIsolation": "isolated" }
|
||
```
|
||
|
||
### 外部样式类
|
||
|
||
```javascript
|
||
// 组件
|
||
Component({
|
||
externalClasses: ['my-class']
|
||
})
|
||
```
|
||
|
||
```xml
|
||
<!-- 组件 wxml -->
|
||
<view class="my-class">这段文本的颜色由外部决定</view>
|
||
|
||
<!-- 使用时 -->
|
||
<my-component my-class="red-text"/>
|
||
```
|
||
|
||
## 组件间通信
|
||
|
||
### 父 → 子:properties
|
||
|
||
```xml
|
||
<my-component prop-a="{{dataA}}" prop-b="staticValue"/>
|
||
```
|
||
|
||
### 子 → 父:triggerEvent
|
||
|
||
```javascript
|
||
// 子组件
|
||
Component({
|
||
methods: {
|
||
onTap() {
|
||
this.triggerEvent('myevent', { value: 'data' }, {
|
||
bubbles: false, // 是否冒泡
|
||
composed: false, // 是否穿越组件边界
|
||
capturePhase: false // 是否有捕获阶段
|
||
})
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
```xml
|
||
<!-- 父组件/页面 -->
|
||
<my-component bind:myevent="onMyEvent"/>
|
||
<!-- 或 bindmyevent="onMyEvent" -->
|
||
```
|
||
|
||
```javascript
|
||
// 父组件/页面
|
||
Page({
|
||
onMyEvent(e) {
|
||
e.detail // { value: 'data' }
|
||
}
|
||
})
|
||
```
|
||
|
||
### 父获取子实例:selectComponent
|
||
|
||
```xml
|
||
<my-component id="the-id" class="the-class"/>
|
||
```
|
||
|
||
```javascript
|
||
const child = this.selectComponent('#the-id')
|
||
// 或 this.selectComponent('.the-class')
|
||
child.setData({ ... })
|
||
child.someMethod()
|
||
```
|
||
|
||
## behaviors(代码复用)
|
||
|
||
类似 mixins / traits。
|
||
|
||
```javascript
|
||
// my-behavior.js
|
||
module.exports = Behavior({
|
||
behaviors: [], // 可以引用其他 behavior
|
||
properties: {
|
||
myBehaviorProperty: { type: String }
|
||
},
|
||
data: {
|
||
myBehaviorData: {}
|
||
},
|
||
attached() {},
|
||
methods: {
|
||
myBehaviorMethod() {}
|
||
}
|
||
})
|
||
```
|
||
|
||
```javascript
|
||
// 组件中使用
|
||
const myBehavior = require('my-behavior')
|
||
Component({
|
||
behaviors: [myBehavior],
|
||
// 组件自身的 properties/data/methods 会与 behavior 合并
|
||
// 同名字段:组件 > behavior > 更早的 behavior
|
||
// 同名生命周期:都会执行(behavior 先于组件)
|
||
})
|
||
```
|
||
|
||
### 内置 behaviors
|
||
|
||
| behavior | 说明 |
|
||
|----------|------|
|
||
| `wx://form-field` | 使组件像表单控件,form 可识别 |
|
||
| `wx://form-field-group` | form 识别组件内部所有表单控件(2.10.2+) |
|
||
| `wx://form-field-button` | form 识别组件内部 button(2.10.3+) |
|
||
| `wx://component-export` | 自定义 selectComponent 返回值(2.2.3+) |
|
||
|
||
## 纯数据字段
|
||
|
||
不用于渲染的数据,不会参与 setData 传输,提升性能。
|
||
|
||
```javascript
|
||
Component({
|
||
options: {
|
||
pureDataPattern: /^_/ // 以 _ 开头的字段为纯数据
|
||
},
|
||
data: {
|
||
a: true, // 普通数据,参与渲染
|
||
_b: true // 纯数据,不参与渲染
|
||
}
|
||
})
|
||
```
|
||
|
||
## 组件间关系 relations
|
||
|
||
```javascript
|
||
// custom-ul
|
||
Component({
|
||
relations: {
|
||
'./custom-li': {
|
||
type: 'child',
|
||
linked(target) {}, // 子组件 attached 时
|
||
linkChanged(target) {},
|
||
unlinked(target) {} // 子组件 detached 时
|
||
}
|
||
}
|
||
})
|
||
|
||
// custom-li
|
||
Component({
|
||
relations: {
|
||
'./custom-ul': {
|
||
type: 'parent',
|
||
linked(target) {},
|
||
linkChanged(target) {},
|
||
unlinked(target) {}
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
**type 可选值**:`parent` / `child` / `ancestor` / `descendant`
|
||
|
||
## 抽象节点 componentGenerics
|
||
|
||
```json
|
||
// selectable-group.json
|
||
{
|
||
"componentGenerics": {
|
||
"selectable": {
|
||
"default": "path/to/default"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
```xml
|
||
<!-- selectable-group.wxml -->
|
||
<view wx:for="{{labels}}">
|
||
<selectable disabled="{{false}}"></selectable>
|
||
</view>
|
||
|
||
<!-- 使用时指定具体组件 -->
|
||
<selectable-group generic:selectable="custom-radio"/>
|
||
```
|
||
|
||
## 用 Component 构造器构造页面
|
||
|
||
```javascript
|
||
Component({
|
||
properties: {
|
||
paramA: Number, // 接收页面参数 ?paramA=123
|
||
paramB: String
|
||
},
|
||
methods: {
|
||
onLoad() {
|
||
this.data.paramA // 123
|
||
},
|
||
onShow() {},
|
||
onPullDownRefresh() {}
|
||
// 页面生命周期写在 methods 中
|
||
}
|
||
})
|
||
```
|
||
|
||
对应 json 需包含 `usingComponents`:
|
||
```json
|
||
{ "usingComponents": {} }
|
||
```
|
||
|
||
好处:可以使用 behaviors 提取所有页面公用代码。
|
||
|
||
## 在线查询
|
||
|
||
如需更详细信息,可抓取:
|
||
- Component 参考:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html
|
||
- Behavior 参考:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Behavior.html
|
||
- 组件生命周期:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/lifetimes.html
|