2、创建第一个组件:组件文件结构、Component构造器、组件的生命周期
好,咱们正式开始动手写组件了。
这一节我会带你从零创建一个微信小程序组件。说白了,就是搞清楚三件事:文件怎么放、代码怎么写、生命周期怎么用。这三件事搞明白了,后面所有组件开发都是换汤不换药。
2.1 组件文件结构:别小看这四件套
先说说文件结构。我个人习惯把组件都放在 components/ 目录下,每个组件一个独立文件夹。比如我要写一个「用户卡片」组件,结构是这样的:
components/
└── user-card/
├── user-card.json // 组件配置文件
├── user-card.wxml // 组件模板
├── user-card.wxss // 组件样式
└── user-card.js // 组件逻辑
嗯,这里要注意:四个文件缺一不可。我在项目中遇到过有人只写了三个文件,结果编译报错找了半天——其实就是少了 .json 文件。
关键点:组件的 .json 文件里必须声明 "component": true,否则小程序不认它是组件。
来看 user-card.json 怎么写:
{
"component": true,
"usingComponents": {}
}
就这么简单。第一行告诉小程序「我是组件」,第二行是组件里如果还要嵌套其他组件,就在这里注册。
2.2 Component 构造器:组件的灵魂
组件的逻辑写在 .js 文件里。和页面用 Page() 不同,组件用的是 Component() 构造器。
为什么不用 Page()?你想想看,页面是独立的一整屏,组件是要被复用的零件。两者的生命周期、数据传递方式都不一样。我刚开始学的时候也犯过混,直接用 Page() 写组件,结果发现 onLoad 根本不触发——因为组件根本没有 onLoad。
来看一个完整的组件代码:
// components/user-card/user-card.js
Component({
// 组件的属性列表(外部传入的数据)
properties: {
username: {
type: String,
value: '默认用户'
},
avatar: {
type: String,
value: '/images/default-avatar.png'
},
level: {
type: Number,
value: 1
}
},
// 组件的内部数据
data: {
showBadge: false
},
// 组件的方法
methods: {
onTapCard() {
// 触发自定义事件,通知父组件
this.triggerEvent('cardtap', {
username: this.properties.username
})
}
},
// 组件的生命周期
lifetimes: {
created() {
console.log('组件被创建了')
},
attached() {
console.log('组件被挂载到页面了')
},
ready() {
console.log('组件渲染完成了')
},
detached() {
console.log('组件被移除了')
}
}
})
个人经验:我习惯在 attached 里做数据初始化,而不是 created。因为 created 时组件还没挂载到页面,拿不到一些上下文信息。曾经有个项目,我在 created 里调用了 wx.getSystemInfoSync(),结果某些机型上报错——后来改到 attached 就没事了。
2.3 组件的生命周期:什么时候该干什么事
组件的生命周期比页面简单,但有几个关键节点必须掌握。我整理了一张表:
| 生命周期 | 触发时机 | 常用场景 |
|---|---|---|
created |
组件实例刚被创建 | 初始化一些只读数据,不要操作 setData |
attached |
组件被挂载到页面节点树 | 发起网络请求、订阅事件、初始化数据 |
ready |
组件布局渲染完成 | 获取节点信息(如 wx.createSelectorQuery) |
detached |
组件从页面被移除 | 清理定时器、取消事件订阅 |
这里有个坑,我曾经踩过:在 created 里调用 setData。为什么不行?因为 created 时组件还没和页面建立联系,setData 不会生效,而且会报 warning。如果你需要在创建时设置数据,要么在 properties 里给默认值,要么等到 attached 再设。
警告:不要在 created 里使用 this.setData()。我曾经在一个项目中这样写了,结果数据一直不更新,排查了半小时才发现是生命周期用错了。
2.4 组件生命周期与页面生命周期的关系
组件是嵌在页面里的,所以两者的生命周期有先后顺序。举个例子:
- 页面
onLoad触发 - 页面开始渲染,遇到组件标签
- 组件
created→attached→ready - 页面
onReady触发
也就是说,组件的 ready 一定在页面的 onReady 之前。这个顺序在实战中很有用。比如你要在页面加载后获取组件的数据,可以在页面的 onReady 里通过 this.selectComponent() 去拿,这时候组件肯定已经准备好了。
2.5 完整示例:跑起来看看
最后,咱们把上面讲的串起来,写一个能用的组件。假设页面 pages/index/index 要使用 user-card 组件:
第一步:在页面的 .json 里注册组件
// pages/index/index.json
{
"usingComponents": {
"user-card": "/components/user-card/user-card"
}
}
第二步:在页面的 .wxml 里使用组件
<!-- pages/index/index.wxml -->
<user-card
username="张三"
level="5"
bind:cardtap="onCardTap"
/>
第三步:在页面的 .js 里处理组件事件
// pages/index/index.js
Page({
onCardTap(e) {
console.log('点击了用户卡片:', e.detail.username)
}
})
总结一下:创建组件就是三步走——建好四件套文件、用 Component() 写逻辑、在页面里注册并使用。生命周期记住 attached 做初始化、detached 做清理,基本不会出大问题。
下一节咱们会深入组件的 properties 和 data,讲讲数据怎么传、怎么改。到时候我会分享一个我在电商项目里踩过的数据传递大坑——保证让你印象深刻。