一、自定义组件
自定义组件是小程序中用于代码复用和模块化开发的重要机制,允许开发者将页面中可复用的部分封装为独立组件,提高开发效率和代码可维护性。
1. 组件的创建与目录结构
自定义组件由4个文件组成,与页面结构类似,通常放在项目的components目录下:
.json:组件配置文件,需声明该目录为组件,配置{"component": true}。
.wxml:组件的结构模板,定义组件的UI布局。
.wxss:组件的样式文件,用于美化组件UI,默认情况下组件样式只对当前组件生效(样式隔离)。
.js:组件的逻辑文件,通过Component()函数定义组件的属性、数据、方法等。
示例:创建一个名为"my-component"的组件,目录结构为/components/my-component/,其中my-component.json配置如下:
{
"component": true,
"usingComponents": {} // 若组件内部使用其他组件,需在此声明
}
2. 组件的使用步骤
在需要使用组件的页面或父组件的.json文件中,通过usingComponents配置组件路径,格式为"组件名": "组件相对路径"。
// 页面的.json文件
{
"usingComponents": {
"my-component": "/components/my-component/my-component"
}
}
在页面或父组件的.wxml中,像使用内置组件一样使用自定义组件,如<my-component></my-component>。
3. 组件的数据、属性与方法
(1)数据(data)
组件内部的私有数据,用于组件的视图渲染,定义在Component()的data对象中,与页面数据用法一致,可通过this.setData()修改。
Component({
data: {
count: 0 // 组件内部数据
}
})
(2)属性(properties)
用于接收父组件传递的数据,类似于HTML元素的属性,可指定类型、默认值和值验证。
Component({
properties: {
// 简化写法
title: String,
// 完整写法
maxCount: {
type: Number, // 类型:Number、String、Boolean等
value: 10, // 默认值
observer(newVal, oldVal) { // 属性值变化时的回调
// 处理新值
}
}
}
})
父组件传递属性:<my-component title="标题" max-count="20"></my-component>(注意属性名在WXML中需用连字符写法)。
(3)方法(methods)
组件的自定义方法,用于处理组件的逻辑,定义在Component()的methods对象中。
Component({
methods: {
increment() {
this.setData({ count: this.data.count + 1 });
// 触发自定义事件向父组件传递数据
this.triggerEvent('change', { newCount: this.data.count });
}
}
})
4. 组件的事件与通信
(1)子组件向父组件通信
通过triggerEvent方法触发自定义事件,父组件通过bind:事件名监听。
// 子组件中触发事件
this.triggerEvent('customEvent', { data: '数据' }, { bubbles: false }); // 第三个参数为事件选项
// 父组件中监听
<my-component bind:customEvent="handleEvent"></my-component>
// 父组件的.js中定义处理函数
Page({
handleEvent(e) {
console.log(e.detail.data); // 获取子组件传递的数据
}
})
(2)父组件向子组件通信
通过属性传递数据(见3.2属性部分),或通过selectComponent方法获取子组件实例后直接调用子组件方法。
// 父组件中获取子组件实例
const child = this.selectComponent('.my-component-class'); // 传入子组件的选择器
child.increment(); // 调用子组件的方法
5. 组件的插槽(slot)
插槽用于在组件内部预留位置,允许父组件插入自定义内容,类似HTML的slot元素。
默认插槽:组件中定义<slot></slot>,父组件使用组件时在标签内写入内容即可插入。
// 组件的.wxml
<view><slot></slot></view>
// 父组件使用
<my-component><text>插入的内容</text></my-component>
具名插槽:为插槽指定name属性,父组件通过slot属性指定插入的插槽。
// 组件的.wxml
<view>
<slot name="header"></slot>
<slot name="content"></slot>
</view>
// 父组件使用
<my-component>
<text slot="header">头部内容</text>
<text slot="content">主体内容</text>
</my-component>
6. 组件的生命周期
组件特有的生命周期函数,定义在Component()的lifetimes对象中(也可直接定义在Component对象中,但推荐用lifetimes)。
Component({
lifetimes: {
created() { // 组件实例刚被创建时执行,此时不能调用setData
console.log('组件被创建');
},
attached() { // 组件实例进入页面节点树时执行,常用初始化操作
console.log('组件挂载');
},
ready() { // 组件布局完成时执行
console.log('组件就绪');
},
detached() { // 组件实例被从页面节点树移除时执行,用于清理工作
console.log('组件卸载');
}
}
})
二、使用npm包
小程序支持通过npm安装第三方包(如UI组件库、工具库等),扩展小程序功能,需通过开发者工具构建后使用。
1. 步骤
初始化npm:在项目根目录打开终端,执行npm init -y生成package.json文件。
安装npm包:执行npm install 包名 --save,例如安装Vant Weapp(小程序UI组件库):npm i @vant/weapp -S --production。
构建npm:打开微信开发者工具,点击菜单栏“工具”→“构建npm”,生成miniprogram_npm目录(存放构建后的包)。
使用npm包:在页面或组件的.json文件中配置usingComponents,引入包中的组件。
// 页面.json
{
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
}
// 页面.wxml中使用
<van-button type="primary">按钮</van-button>
2. 注意事项
仅支持符合小程序规范的npm包,部分依赖浏览器环境的包(如操作DOM的包)无法使用。
安装包后需重新构建npm才能生效,更新包版本后也需重新构建。
可在项目设置中勾选“使用npm模块”确保npm功能启用。
三、全局数据共享
全局数据共享(状态管理)用于解决多个页面或组件间的数据共享问题,避免多层级组件通信的繁琐,常用方案有mobx-miniprogram(推荐)和Redux。
1. mobx-miniprogram 用法
mobx是基于响应式编程的状态管理库,小程序中需配合mobx-miniprogram-bindings使用。
安装依赖:npm install mobx-miniprogram mobx-miniprogram-bindings --save。
创建store:新建store目录,定义全局数据和方法。
// store/index.js
import { observable, action } from 'mobx-miniprogram';
export const store = observable({
// 全局数据
num: 0,
// 计算属性(根据其他数据派生)
get doubleNum() {
return this.num * 2;
},
// 修改数据的方法(需用action包裹)
updateNum: action(function(val) {
this.num = val;
})
});
在页面中使用:
// 页面.js
import { createStoreBindings } from 'mobx-miniprogram-bindings';
import { store } from '../../store/index';
Page({
onLoad() {
// 绑定store
this.storeBindings = createStoreBindings(this, {
store,
fields: ['num', 'doubleNum'], // 需要用到的数据
actions: ['updateNum'] // 需要用到的方法
});
},
onUnload() {
// 解绑store
this.storeBindings.destroy();
},
handleClick() {
this.updateNum(10); // 调用store中的方法
}
})
// 页面.wxml
<view>{{ num }}</view>
<view>{{ doubleNum }}</view>
在组件中使用:类似页面,需引入storeBindingsBehavior。
// 组件.js
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings';
import { store } from '../../store/index';
Component({
behaviors: [storeBindingsBehavior], // 引入行为
storeBindings: {
store,
fields: ['num'],
actions: ['updateNum']
}
})
四、分包
分包是将小程序代码按功能拆分到不同包中,主包包含启动页和公共资源,分包在用户访问时才下载,减少首次启动加载时间。
1. 配置
在app.json中通过subpackages(或subPackages)配置分包,每个分包包含root(分包根目录)和pages(分包页面路径)。
{
"pages": [ // 主包页面(必填,至少包含一个页面)
"pages/index/index",
"pages/logs/logs"
],
"subpackages": [
{
"root": "packageA", // 分包A根目录
"pages": [ // 分包A的页面
"pages/cat/cat",
"pages/dog/dog"
]
},
{
"root": "packageB", // 分包B根目录
"pages": [
"pages/apple/apple"
]
}
]
}
目录结构示例: - pages(主包) - packageA(分包A) - pages - packageB(分包B) - pages
2. 分包加载规则
主包必须包含小程序的启动页面(第一个页面)。
用户首次打开小程序时,仅下载主包;进入分包页面时,自动下载对应分包。
分包之间不能相互引用资源,但可引用主包资源;主包可引用所有分包资源。
3. 独立分包
独立分包可独立于主包运行,不依赖主包,适合快速启动的场景(如广告页、活动页),配置independent: true。
"subpackages": [
{
"root": "packageC",
"pages": ["pages/activity/activity"],
"independent": true // 声明为独立分包
}
]
4. 分包预下载
在app.json中通过preloadRule配置,指定进入某页面时预下载其他分包,提升后续访问速度。
"preloadRule": {
"pages/index/index": { // 进入主包首页时
"network": "all", // 在任意网络下预下载
"packages": ["packageA"] // 预下载分包A
}
}
5. 限制
主包体积≤2MB,单个分包体积≤2MB,所有分包总体积≤20MB(不同基础库版本可能有调整)。