2021版小程序开发4——基础加强
2021版小程序开发4——基础加强
学习笔记 2025
- 自定义组件
- 组件中behaviors的作用
- 安装和使用vant-weapp组件库
- 使用MobX实现全局数据共享
- 对小程序的API进行Promise化
具体的内容还包括:使用npm包、全局数据共享、分包和自定义tabBar的案例;
1 自定义组件
创建自定义组件
创建/components
目录,继续创建子目录(目录名为组件名),右击子目录新建Component
填入组件名即可;每个组件也会自动创建四个组成文件;
引入自定义组件:
- 局部引用:在页面的json配置文件中配置
- 全局引用:在全局的json配置文件中配置
{
"usingComponents" : {
'ms-test': "/components/test/test" // 组件路径 不要带后缀
}
}
组件和页面的区别:组件的js和json文件 明显区别于页面的
- 组件的json中,需要声明属性
"component":true
- 组件的js中调用的是
Component()
函数,而页面的js中调用的是Page()
函数; - 组件的事件处理函数需要定义到
methods
节点中,而页面的函数只需要定义在和data节点平级的节点中即可);
内部方法建议以下划线开头;
在组件和引用组件的页面中 尽量使用
class选择器
,不要使用id、属性、标签选择器;因为只有class选择器受样式隔离
的影响;
全局/页面样式不会影响组件内样式;
修改组件样式的隔离选项
修改组件样式的隔离选项:
- 自定义组件的样式隔离特性能够防止组件内外样式相互干扰;
- 如果希望在外部能够影响组件内部的样式,可以修改组件的样式隔离选项:styleIsolation
- isolated 默认值
- apply-shared 页面可以影响组件
- shared 页面和组件样式可相互影响,而且组件中样式还会影响其他设置了apply-shared或shared的组定义组件;
// 在组件js中配置
Component({
options: {
styleIsolation: 'isolated'
}
})
// 或在组件.json中配置
{
'styleIsolation': 'isolated'
}
自定义组件的数据、方法和属性
用于组件模版渲染的私有数据,需要定义到data节点
中(也页面一样);
组件中,事件处理函数和自定义方法都需要定义到methods
节点中;
组件中,properties
是组件的对外属性,用来接受外界传递到组件的数据:
- 使用组件时,通过属性传值;
{
properties: {
max:{
type: Number, // 数据类型
value: 10 // 默认值
},
min: Number // 无需指定默认值时的简化用法
}
}
// <my-test max="11"></my-test>
和Vue比,比较特别的一点是,小程序组件中的properties和data数据都是可读可写的;
组件中的数据监听器
监听属性变化,执行特定操作;(类似Vue中watch)
小程序中监听的基本语法格式:
Component({
// 需要声明一个 observers 配置对象
observers: {
// 可以在一个监听器中 同时监听多个字段的变化
"字段1, 字段2": function("字段1的新值,字段2的新值"){
// 触发方式:
// 为字段1 赋值
// 为字段2 赋值
// setData为对象赋值
}
}
})
关于setData/observers:使用嵌套对象的子属性赋值/监听的方法;
data: {
rgb: {
r: 0,
g: 0
},
fullColor: ''
}
...
this.setData({
// 为嵌套对象的子属性赋值的方式
'rgb.r': 255
})
...
observers: {
'rgb.r, rgb.g' : function(r, g){
this.setData(
fullColor: `${r}, ${g}`
)
}
}
通配符监听:
- 使用通配符
**
来监听对象中所有属性的变化;
observers: {
'rgb.**': function(obj){
this.setData({
fullColor: `${obj.r}, ${obj.g}`
})
}
}
纯数据字段
指不用于界面 渲染的data字段(具体的:既不会展示在界面上,也不会传递给其他组件,仅在当前页面的内部使用);
这样的数据适合被定义为纯数据字段,有助于提升页面的更新性能;
使用:
- 在Component的options节点中,指定
pureDataPattern
为一个正则表达式,符合这个正则的字段都将成为纯数据字段
(可以是一个对象);
Component({
options:{
pureDataPattern: /^_/ // 以下划线开头的字段
},
data: {
a: true,
_b: true, // pureData
_rgb: { // pureData
r: 0,
g: 0,
}
}
})
快捷选中: cmd+d
2 组件生命周期
- created:刚创建
- 不能调用setData,因为data还没初始化
- 可以给this添加一些自定义的属性字段
attached
: 刚被放入页面节点树,还没渲染- 可以发送网络请求;
- ready:在视图层布局完成之后执行
- moved:组件实例被移动到节点树的另一个位置时执行
detached
:从页面节点树移除- 清理事件监听;
- error:组件方法异常的时候执行
排列顺序也是执行顺序;
小程序组件的生命周期函数可以在lifetimes
字段内进行声明;
Components({
lifetimes:{
attached(){},
detached(){},
}
})
组件所在页面的生命周期
在自定义组件的行为需要依赖页面状态的变化的时候,可能需要在组件中监听组件所在页面的生命周期;
组件能使用的页面生命周期函数:
- 这些函数需要定义在
pageLieftimes
节点中; - show:组件所在页面被显示的时候
- hide:组件所在页面被隐藏的时候
- resize::组件所在页面的尺寸变化的时候
Components({
pageLieftimes:{
show(){
Math.floor(Math.random() * 256) // 0~255
},
hide(){},
resize(){},
}
})
3 自定义组件插槽
- 提供
<slot></slot>
节点,相当于占位符,用于承载组件使用者提供的wxml结构;
<!-- test组件内 -->
<view>
<!-- 单插槽 -->
<slot></slot>
</view>
<!-- 使用组件时 -->
<test>
<view>替换组件内占位符</view>
</test>
如果需要让组件支持多个插槽,只需要在options
节点配置multipleSlots
属性为true;每个slot占位以不同的name区分;
<!-- test组件内 -->
<view>
<slot name="header"></slot>
<view>Content</view>
<slot name="footer"></slot>
</view>
<!-- 使用组件时 -->
<test>
<view slot="header">替换组件内占位符header</view>
<view slot="footer">替换组件内占位符footer</view>
</test>
4 父子组件间通信
父子组件间传值的三种方式:
- 通过属性绑定传值,
父向子传值
,需要指定属性,仅能传递json兼容的数据(无法传递方法); - 事件绑定,
子向父传值
;- 在父组件中定义一个回调函数,然后传递给子组件;
- 在子组件中 调用
this.triggerEvent('自定义事件名称', {参数对象})
,将数据发送到父组件 - 父组件js中,通过
e.detail
获取子组件所传递的数据,可以是任意数据类型(如方法);
- 父组件中可通过
this.selectComponent()
方法获取子组件实例,以直接访问子组件的任何数据和方法;
属性绑定:
- 父组件中使用子组件并绑定数据(注意这里与vue不同,小程序的wxml中使用的是Mustache语法,如
<test title="{{use_title}}"></test>
); - 子组件中properties节点声明相应的属性名;
- 这个绑定是单向的,因此如果在子组件中title属性发生了变化,还需要使用子向父传值;
事件绑定:
<!-- 事件绑定 -->
<test bind:sync="syncCountChange"></test>
// 父组件
syncCountChange(e){
e.detail.value
}
// 子组件
methods: {
addCount(){
this.setData({
count: this.properties.count + 1
})
this.triggerEvent('sync', {value: this.properties.count})
}
}
直接获取子组件实例:
this.selectComponent(#id或.class选择器)
<test class="customA"></test>
const childA = this.selectComponent(".customA")
// childA.setData(...)
5 自定义组件的behaviors
自定义组件的behaviors
- 用于实现
组件间
代码共享的特性,类似vue中mixins - 每个组件可以引入多个behaviors,behaviors间也可以相互引用;
每个behavior中都可以包含一组:属性、数据、生命周期函数和方法,组件应用它时,它的属性、数据和方法会被合并到组件中;
创建 behavior
// 调用 Behavior() 可以创建一个共享的behavior对象
module.exports = Behavior({
// 属性
properties:{},
// 数据
data:{},
// 方法
methods:{}
// 其他behavior
behaviors:[],
// 生命周期函数
created(){},
attached(){},
ready(){},
moved(){},
detached(){},
})
导入并使用behavior
使用require()
方法导入,挂载后即可访问其中数据或方法;
const myB = require("../../behaviors/my-behavior")
Component({
// 挂载到 behaviors 数组节点中
behaviors:[myB],
})
behaviors中同名字段的覆盖和组合规则:
- 同名数据字段(data)
- 对象重复 合并
- 组件 > 父behavior > 子behavior
- 同名属性(properties)或方法(methods)
- 同名的生命周期函数
具体的可参考小程序官方文档;
6 使用npm包管理三方工具包
3个限制:
- 不支持依赖于Node.js内置库的包;
- 不支持依赖于浏览器内置对象的包;
- 不支持依赖于C++插件的包;
服务于小程序的包没有那么多;虽然有三方包的加持,但是不要忘记小程序本身提供的丰富API;
Vant Weapp
一套开源的小程序UI组件库
;
- https://youzan.github.io/vant-weapp
安装Vant组件库:
- 通过npm安装 建议指定版本,如
@1.3.3
(具体可参考文档)- 在小程序文件目录空白区域,右击打开外部终端
- 初始化npm(会创建一个package.json的配置文件):
npm init -y
- copy命令进行vant安装
- 构建npm包:IDE 工具 构建npm 勾选使用,再本地设置 勾选使用npm模块(以使用三方包);
- 修改app.json配置文件(不使用新版样式,以防止样式冲突)
使用Vant组件库:
// 全局配置使用组件
"usingComponents":{
'van-button': "@vant/weapp/button/index"
}
定义Vant全局的主题样式:
- 使用
CSS变量
(CSS_custom_properties)来实现,具体可参考MDN
文档; - 在 app.wxss中,写入CSS变量,即可对全局生效;
- 参考Vant
定制主题
,可以获取到可用的样式变量;
/* 为了全局 变量都定义在页面的根节点标签page中 */
page {
--变量名: CSS变量值;
@变量名: less变量值;
}
/* 使用时 */
CSS属性 : var(--变量名, 指定变量不存在时的默认值)
API Promise化处理
将基于回调函数的异步api,改造为基于Promise的异步API;
实现方式:
- 依赖
miniprogram-api-promise
三方包,使用npm install --save miniprogram-api-promise@1.0.4
(建议指定版本号)安装; - 每次构建前建议都删除依赖目录:miniprogram_npm目录
- 重新构建:点击
工具
->构建
,重新构建- 在node_modules目录下的包不可以直接使用,构建后,在miniprogram_npm目录下的包才可以使用;
使用:
// 在app.js中 按需导入
import {promisifyAll} form 'miniprogram-api-promise'
// 定义了一个空对象 之后就可以通过wx.p去调用promise化的api(wxp和wx.p是同一个对象)
const wxp = wx.p = {}
// 将wx的异步请求全部promise化 之后挂载到wxp上
promisifyAll(wx, wxp)
async getInfo(){
// 解构data赋值给res
const {data: res} = await wx.p.request({
method: "GET",
url: '',
data: {}
})
console.log(res)
}
全局数据共享
即状态管理,是为了解决组件间数据共享的问题,类似Vue中如vuex
小程序中可使用两个包:mobx-miniprogream
配合mobx-miniprogream-bindings
实现全局数据共享
- mobx-miniprogream 用于创建 store实例
- mobx-miniprogream-bindings 用于把store中的数据共享方法 绑定到组件或页面中使用
npm安装,之后:
- 创建一个store目录
- 其中再创建一个 store.js,专门创建Store的实例对象
// store.js
import {observable, action} form 'mobx-miniprogream'
// 按需导出
export const store = observable({
// 挂载需要共享的数据
// 数据
num1: 1,
num2: 1,
// 计算属性
get sum(){
return this.num1 + this.num2
},
// actions
// 专门用来定义 修改数据的方法
dosomething: action(function (step){
this.num1 += step
})
})
- 将Store中的成员绑定到页面中
import { createStoreBindings } from 'mobx-miniprogream-bindings'
import {store} from "../../store/store"
Page({
onLoad: function(){
//
this.storeBindings = createStoreBindings(this, {
store,
fields: ['num1', 'num2', 'sum'], // 绑定的字段
actions: ['dosomething'] // 绑定的方法
})
},
onUnload: function() {
this.storeBindings.destroyStoreBindings()
}
})
组件中的绑定略有不同:
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from '../../store/store'
Component({
behaviors: [storeBindingsBehavior],
// 配置对象 storeBindings
storeBindings: {
store,
fields: {
numA: (store) => store.num1,
numB: 'num2'
},
actions: {
dosome: 'dosomething'
}
}
})
7 分包
指把一个完整的小程序项目,按需求划分为不同的子包,在构建时打包成不同的分包,用户在使用的时候按需加载;
默认是不分包的;
一个主包+多个分包:
- 主包:一般包含启动页面或多个TabBar页面,以及所有分包都要用到的一些公共资源;
- 分包:只当前分包相关的页面和私有资源;
分包可以引用主包内的公共资源;
分包加载:
- 小程序启动时,默认下载主包并启动主包内页面;
- tabBar只能放到主包中;
- 当进入分包某页面时,才会下载对应分包;
分包体积限制:
- 所有分包不超过16M
- 单个包不超过2M
配置分包:
- 项目目录解构的变化;
- 主包: pages
- 分包A: packageA/pages
- 分包B: packageB/pages
- app.json配置节点(修改后保存会自动创建文件目录);
- pages
- subpackages
"pages": [ "pages/index/index", "pages/logs/logs"],
"subpackages":[{
"root": "packageA", // 分包A的根目录
"pages": ["pages/indexA/indexA", "pages/logsA/logsA"] // 相对当前root的页面存放路径
},{
"root": "packageB",
"name": "pkg2", // 分包别名
"pages": ["pages/indexB/indexB", "pages/logsB/logsB"]
}]
在项目基本信息面板,可以看到各个分包的体积;
打包原则:
- 按subpackages配置进行分包,之外的目录会被打包到主包中;
- 主包有自己的pages(即最外层的pages);
- tabBar必须在主包内;
- 分包之间不能相互嵌套;
独立分包:
- 一种特殊的分包,只不过可以独立于主包二单独运行;
- 正常分包,只能通过主包跳转(需要先下载主包);
- 而独立分包,可以在小程序启动后直接载入(无需依赖主包);
- 一个小程序中可以有多个独立分包;
- 独立分包与主包(包括主包内的公共资源)和其他分包(包括独立分包)间是
隔绝
的,不能相互引用彼此资源;
配置独立分包:
- 在分包配置中增加
"independent": true
即可;
分包预下载:
- 在进入指定页面时可以配置触发该行为,将可能使用的后续分包页面提前下载;
- 使用
preloadRule
节点进行定义;
"preloadRule": {
"pages/myself/myself": {
// 在进入 myself页面的时候 触发加载以下分包
"network": "all", // 网络模式 默认为wifi
"packages": ["pkg2"] // 分包别名
}
}
同一个主/分包中的页面享用共同的预下载大小限额2M,如主包的tabBar三个页面分别预下载了体积为1M的分包,此时主包就会下载3M的分包,这是不允许的(会失败);
8 自定义tabBar实现更丰富的功能
- 自定义组件:参考小程序官方文档,自定义tabBar(注意这是一个组件)
- Vant组件库:提供了一个可用的Tabbar
- 切换tab,拿到change的tab索引,调用wx.switchTab方法;
- 把tab索引也放到store中(包括相应的修改值的方法),以解决索引无法切换的问题;
- MobX数据共享:参考小程序官方文档,扩展能力,框架扩展;
- 组件样式隔离:要覆盖tabBar的默认样式,需要修改自定义Tabbar组件样式隔离选项的值,
- 组件数据监听器:监听Store中的字段,为组件内变量赋值
- 组件的behaviors
- Vant样式覆盖:参考Vant文档,
样式覆盖
;
observers: {
'sum': function(val){
this.setData({
// 为datalist数组下标为1的对象的info属性赋值
'datalist[1].info': val
})
}
}