微信小程序第四天
1. 目标
- 能够知道如何自定义小程序组件
- 能够知道小程序组件中
behaviors
的作用
2. 组件的创建和引用
2.1 创建自定义组件
-
创建组件
- 在项目的根目录中,鼠标右键,创建
components
->test
文件夹 - 在新建的
components
->test
文件夹上,鼠标右键,点击新建 Component
- 键入组件的名称之后回车,会自动生成组件对应的 4 个文件,后缀名分别为
.js
,.json
,.wxml
和.wxss
- 注意:为了保证目录结构的清晰,建议把不同的组件,存放到单独目录中
- 在项目的根目录中,鼠标右键,创建
2.2 局部引用组件
-
组件的引用方式
- 组件的引用方式分为
局部引用
和全局引用
局部引用
:组件只能在当前被引用的页面内使用全局引用
:组件可以在每个小程序页面中使用
- 组件的引用方式分为
-
局部引用组件
- 在
页面的 .json
配置文件中引用组件的方式,叫做局部引用
- 在
2.3 全局引入组件
在 app.json
全局配置文件中引用组件的方式,叫做 全局引用
2.5 全局引用和局部引用的区别
- 根据组件的
使用频率
和范围
,来选择合适的引用方式:- 如果某组件
在多个页面中经常被用到
,建议进行全局引用
- 如果某组件只
在特定的页面中被用到
,建议进行局部引用
- 如果某组件
2.6 组件和页面的区别
- 从表面来看,组件和页面都是由
.js
、.json
、.wxml
和.wxss
这四个文件组成的。但是,组件和 页面的.js
与.json
文件有明显的不同:- 组件的 .json 文件中需要声明
"component": true
属性 - 组件的 .js 文件中调用的是
Component()
函数- 整个程序启动调用的是
App()
- 某个页面的渲染调用的是
Page()
- 某个组件的渲染调用的是
Component()
- 整个程序启动调用的是
- 组件的事件处理函数需要定义到
methods
节点中
- 组件的 .json 文件中需要声明
3. 组件的数据、方法和属性
3.1 定义 data 私有数据
-
在小程序组件中,用于组件模板渲染的私有数据,需要定义到
data
节点中
3.2 methods 方法
-
在小程序组件中,事件处理函数和自定义方法需要定义到
methods
节点中
3.3 定义 properties 属性
-
在小程序组件中,
properties
是组件的对外属性,用来接收外界(父组件)传递到组件中的数据
3.4 data 和 properties 的区别
- 在小程序的组件中,
properties
属性和data
数据的用法相同,它们都是可读可写的,只不过:data
更倾向于存储组件的私有数据properties
更倾向于存储外界传递到组件中的数据- 所以, 也不太建议修改
properties
数据,如果要修改properties
的数据, 最好通过子组件通信给父组件的方式实现
3.5 使用 setData 修改 properties 的值
- 由于
data
数据和properties
属性在本质上没有任何区别,因此properties
属性的值也可以用于页面渲染,或使用setData
为properties
中的属性重新赋值
4. 数据监听器
4.1 什么是数据监听器以及其基础语法
-
什么是数据监听器
数据监听器用于监听和响应任何属性和数据字段的变化,从而执行特定的操作。它的作用类似于
vue
中的watch
侦听器。在小程序组件中,数据监听器的基本语法格式如下
4.2 数据监听器的基本用法
-
组件结构
-
组件的
js
代码 -
完整代码
<view> {{ num1 }} + {{ num2 }} = {{ sum }} </view> <button type="warn" bindtap="addNum1">Num + 1</button> <button type="warn" bindtap="addNum2">Num + 2</button>
// components/test/test.js Component({ /** * 组件的初始数据 */ data: { num1: 0, num2: 0, sum: 0 }, // 数据侦听器 observers: { 'num1, num2': function (newNum1, newNum2) { this.setData({ sum: newNum1 + newNum2 }) } }, /** * 组件的方法列表 */ methods: { addNum1 () { this.setData({ num1: this.data.num1 + 1 }) }, addNum2 () { this.setData({ num2: this.data.num2 + 1 }) } } })
4.3 监听对象属性的变化
-
数据监听器支持监听对象中单个或多个属性的变化
<button type="warn" bindtap="changeObj">监听对象的属性</button>
<view>{{ obj.name }}</view>
// components/test/test.js
Component({
// 组件的初始数据
data: {
obj: {
name: 'tom'
}
},
// 数据侦听器
observers: {
'obj.name': function (newName) {
console.log(newName)
}
},
// 方法列表
methods: {
changeObj() {
this.setData({
'obj.name': 'jerry'
})
}
}
})
5. 组件的生命周期
5.1 组件全部的生命周期函数
生命周期 | 参数 | 描述 |
---|---|---|
created | 无 | 在组件实例刚刚被创建时执行 |
attached | 无 | 在组件实例进入页面节点树时执行 |
ready | 无 | 在组件在视图层布局完成后执行 |
moved | 无 | 在组件实例被移动到节点树另一个位置时执行 |
detached | 无 | 在组件实例被从页面节点树移除时执行 |
error | Object Error | 每当组件方法抛出错误时执行 |
5.2 组件主要的生命周期函数
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。
-
最重要的生命周期是
created
,attached
,detached
,包含一个组件实例生命流程的最主要时间点。-
组件实例刚刚被创建好时,
created
生命周期被触发- 此时还不能调用
setData
- 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段
- 此时还不能调用
-
在组件完全初始化完毕、进入页面节点树后,
attached
生命周期被触发this.data
已被初始化完毕- 这个生命周期很有用,绝大多数初始化工作可以在这个时机进行
-
在组件离开页面节点树后,
detached
生命周期被触发- 退出一个页面时,会触发页面内每个自定义组件的
detached
生命周期被触发 - 如果组件还在页面节点树中,则
detached
会被触发。 - 此时适合做一些清理性质的工作
- 退出一个页面时,会触发页面内每个自定义组件的
-
5.3 lifetime 节点
- 生命周期方法可以直接定义在
Component
构造器的第一级参数中,组件的的生命周期也可以在lifetimes
字段内进行声明(这是推荐的方式,其优先级最高)
lifetimes: {
attached () {
console.log('在组件实例进入页面节点树')
},
detached () {
console.log('在组件实例被从页面节点树移除')
}
},
attached () {
console.log('~~~~~在组件实例进入页面节点树')
},
detached () {
console.log('~~~~~在组件实例被从页面节点树移除')
},
/**
* 组件的初始数据
*/
data: {
// rgb 的颜色值对象
_rgb: {
r: 0,
g: 0,
b: 0
},
// 根据 rgb 对象的三个属性,动态计算 fullColor 的值
fullColor: '0, 0, 0'
}
6. 组件所在页面的生命周期
6.1 什么是组件所在页面的生命周期
-
有时,自定义组件的行为依赖于页面状态的变化,此时就需要用到组件所在页面的生命周期
- 例如:每当触发页面的
show
生命周期函数的时候,我们希望能够重新生成一个随机的RGB
颜色值。在自定义组件中,组件所在页面的生命周期函数有如下 3 个,分别是
- 例如:每当触发页面的
生命周期 | 参数 | 描述 |
---|---|---|
show | 无 | 组件所在的页面被展示时执行 |
hide | 无 | 组件所在的页面被隐藏时执行 |
resize | Object Size | 组件所在的页面尺寸变化时执行 |
6.2 pageLifetimes 节点
-
组件所在页面的生命周期函数,需要定义在
pageLifetimes
节点中
6.3 生成随机的 RGB 颜色值
Component({
// 在组件的methods节点中,定义一个随机颜色的方法
methods: {
_randomColor() {
// 设置data中的数据
this.setData({
_rgb: {
r: Math.floor(Math.random() * 256),
g: Math.floor(Math.random() * 256),
b: Math.floor(Math.random() * 256)
}
})
}
},
// 在组件内部的pageLifetimes节点中,监听组件在页面的生命周期函数
pageLifetimes: {
// 在页面被展示的时候,调用该方法
show() {
this._randomColor()
},
hide() { }, // 页面被隐藏
resize() { } // 页面尺寸变化
}
})
7. 插槽
7.1 什么是插槽
- 在自定义组件的
wxml
结构中,可以提供一个 节点(插槽),用于承载组件使用者提供的wxml
结构
- 其实插槽, 说的通俗一些, 就是
子组件挖坑,父组件填坑
的过程。由父组件在使用子组件的时候, 决定子组件内部的某一些布局展示- 子组件通过挖坑
- 父组件通过组件标签中间的内容来填坑
7.2 单个插槽
- 在小程序中,默认每个自定义组件中只允许使用一个
slot
占位,这种个数上的限制叫做单个插槽- 默认情况下,一个组件的
wxml
中只能有一个slot
- 需要使用多
slot
时,可以在组件js
中声明启用 - 注意:小程序中目前只有默认插槽和多个插槽,暂不支持作用域插槽
- 默认情况下,一个组件的
7.3 启动多个插槽
在小程序的自定义组件中,需要使用多 插槽时,可以在组件的 .js
文件中
7.4 定义多个插槽
可以在组件的 .wxml
中使用多个 标签,以不同的 name
来区分不同的插槽
8. 父子组件通讯
8.1 了解父子组件之间通信的 3 个方式
-
属性绑定
- 用于父组件向子组件的指定属性设置数据,仅能设置
JSON
兼容的数据(只能传递数据,不能传递方法)
- 用于父组件向子组件的指定属性设置数据,仅能设置
-
事件绑定
- 用于子组件向父组件传递数据,可以传递任意数据(包括数组和方法)
-
获取组件实例
- 父组件还可以通过
this.selectComponent()
获取子组件实例对象这样就可以直接访问子组件的任意数据和方法
- 父组件还可以通过
8.2 属性绑定
- 传递数据
- 属性绑定用于实现父向子传值,而且只能传递普通类型的数据,无法将方法传递给子组件
- 接收数据
- 子组件在 properties 节点中声明对应的属性并使用
8.3 实现子组件的 count 属性自增 + 1
data: {
count: 0
}
addCount() {
this.setData({
count: this.data.count + 1
})
}
8.4 了解事件绑定的 4 个核心实现步骤
- 事件绑定用于实现子向父传值,可以传递任何类型的数据。使用步骤如下:
- 在
父组件
的js
中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件 - 在
父组件
的wxml
中,通过自定义事件的形式,将步骤 1 中定义的函数引用,传递给子组件 - 在
子组件
的js
中,通过调用this.triggerEvent('自定义事件名称', {/* 参数对象 */})
,将数据发送到父组件 - 在父组件的
js
中,通过e.detail
获取到子组件传递过来的数据
- 在
8.5 了解事件绑定的核心实现代码
-
步骤 1: 在父组件的
js
中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件 -
步骤 2:在父组件的
wxml
中,通过自定义事件的形式,将 步骤 1 中定义的函数引用,传递给子组件 -
步骤 3:在
子组件
的js
中,通过调用this.triggerEvent('自定义事件名称', {/* 参数对象 */})
,将数据发送到父组件 -
步骤 4:在父组件的
js
中,通过e.detail
获取到子组件传递过来的数据
8.6 使用 selectComponent 获取组件实例
可在父组件里调用 this.selectComponent("id或class选择器")
,获取子组件的实例对象,从而直接访问子组件的任意数据和方法。调用时需要传入一个选择器
,例如 this.selectComponent(".my-component")
9. 使用 npm
9.1 小程序对 npm 的支持和限制
目前,小程序中已经支持使用 npm 安装第三方包,从而来提高小程序的开发效率。但是,在小程序中使用 npm 包有如下 3 个限制:
-
不支持依赖于 Node.js 内置库的包
- 不支持依赖于 Node 核心模块的包
- fs Node 文件系统模块 – 不支持
- path Node 路径模块 – 不支持
-
不支持依赖于浏览器内置对象的包
- 小程序的宿主环境是微信,不存在 DOM 和 DOM
- 所以依赖于内置对象的包,也不能够使用
-
不支持依赖于 C++ 插件的包
总结:虽然 npm 上的包有千千万,但是能供小程序使用的包却“为数不多”
9.2 了解什么是 vant Weapp
Vant
是有赞前端团队开源的移动端组件库,于 2016 年开源,已持续维护 4 年时间。Vant
对内承载了有赞所有核心业务,对外服务十多万开发者,是业界主流的移动端组件库之一- 采用
MIT
开源许可协议,对商业使用比较友好 - 扫描下方小程序二维码,体验组件库示例
9.3 安装 Vant 组件库
在小程序项目中,安装 Vant 组件库主要分为如下几步
-
通过
npm
安装- 注意:项目目录不能存在中文!不能存在中文!不能存在中文!否则会报错 !
npm init -y
npm i @vant/weapp@1.3.3 -S --production
-
构建
npm
包功能如果提示构建失败,可以将微信开发者工具重启,重启以后,运行小程序项目,查看是否还报错
- 建议先点击
微信开发者工具
-->详情
-->本地设置
-->使用 npm 模块
- 然后点击
微信开发者工具
-->菜单栏
-->工具
-->构建npm
- 提示
构建成功,耗时 xxx 秒
且控制台没有任何的错误,说明包构建成功,可以进行使用- 否则就需要把
node_modules
、miniprogram_npm
删除 - 删除以后,重新安装包,并点击
工具
-->构建npm
,进行重新构建
- 否则就需要把
- 建议先点击
-
修改
app.json
- 将
styles: v2
进行移除,防止小程序本身的 UI 样式和 Vant 的组件样式库 冲突
- 将
详细的操作地址:安装 vant Weapp
9.4 使用 Vant 组件
安装完 Vant
组件库之后,可以在 app.json
的 usingComponents
节点中引入需要的组件,即可在 wxml
中直接使用组件
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
<van-button type="default">默认按钮</van-button>
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
9.5 定义和使用 CSS 变量 (了解)
Vant Weapp
使用CSS
变量来实现定制主题。 关于CSS
变量的基本用法,请参考MDN
文档 文档地址
-
自定义属性
(有时候也被称作CSS变量
或者级联变量
)是由CSS
作者定义的,它包含的值可以在整个文档中重复使用 -
由自定义属性标记设定值
-
声明一个自定义属性,属性名需要以两个减号(
--
)开始,属性值则可以是任何有效的CSS
值 -
比如:
--main-color: black;
-
-
由var() 函数来获取值
color: var(--main-color);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS 变量</title>
<style>
/* 变量可以声明在某个父级元素中 */
/* 如果是声明在父级元素中, 定义的变量只能够父级元素内部使用 */
.box {
/* css 变量要求,需要以 -- 开头,后面跟上变量名称 */
/* 属性值是合法的 css 属性值即可 */
--main-bg-color: blue;
}
.box {
width: 200px;
height: 200px;
border: 1px solid lightcoral;
/* 如果需要使用 css 变量 */
/* 使用 var() 去调用变量 */
background-color: var(--main-bg-color);
}
.box1 {
width: 200px;
height: 200px;
border: 1px solid lightcoral;
background-color: var(--main-bg-color);
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box1"></div>
</body>
</html>
9.6 使用 CSS 变量定制 Vant 的主题样式 (了解)
在 app.wxss
中,写入 CSS
变量,即可对全局生效
所有可用的颜色变量,请参考 Vant 官方提供的配置文件
9.7 什么是小程序 API 的 Promise 化
-
基于回调函数的异步
API
的缺点-
默认情况下,小程序官方提供的异步 API 都是基于回调函数实现的,例如,网络请求的 API 需要按照如下的方式调用
-
这种代码的缺点是显而易见的, 容易造成回调地狱的问题,代码的可读性、维护性差!而我们就想将这种类型的代码使用
API Promise
化进行改造
-
-
什么是
API Promise
化API Promise
化,指的是通过额外的配置,将官方提供的、基于回调函数的异步API
,升级改造为基于Promise
的异步API
,从而提高代码的可读性、维护性,避免回调地狱的问题
9.8 安装并构建 miniprogram-api-promise
-
在小程序中,实现
API Promise
化主要依赖于miniprogram-api-promise
这个第三方的npm
包npm i --save miniprogram-api-promise@1.0.4
-
下载完成,我们不能直接使用这个包,而是需要再次重新构建npm包
- 建议在构建前先删除原有的
miniprogram_npm
- 然后再点击工具,构建
npm
- 建议在构建前先删除原有的
-
如果删除了
miniprogram_npm
目录,构建还是失败- 需要把
node_modules
、miniprogram_npm
删除 - 删除以后,重新安装包,重新安装以后,再次进行构建
- 需要把
9.9 三个步骤实现 API 的 Promise 化
-
在小程序入口文件中调用一次
promisifyAll()
方法import { promisifyAll } from 'miniprogram-api-promise'
-
声明一个常量,为一个空对象
const wxp = wx.p = {}
-
调用
promisifyAll()
方法promisifyAll(wx, wxp)
-
上述代码是什么含义呢 ?
promisifyAll
: 做的事就是将wx
拥有的属性方法都copy
并改造了一份给了wxp
这个对象- 然而,
wxp
只是当前js
文件的一个常量,只能在当前文件使用 - 因此:我们在
wx
上挂载一个属性p
让他和wxp
指向同一个空对象 - 在其他页面或者组件就可以通过全局对象
wx
点出p
来访问到wxp
- 此时
wx.p
发起异步的请求时,得到的是一个promise
对象 - 那么我们就可以使用
async/await
简化Promise
语法
// 1、导入 promisifyAll 这个方法
import { promisifyAll } from 'miniprogram-api-promise'
// 2、声明一个空对象 wxp
// wxp 只是一个单纯的变量,只能够在当前 js 中使用,外部不能使用
// wx 是微信小程序最顶级的对象
// 只需要往 wx 上去挂载一个对象即可
// 挂载好,以后 wxp 和 wx.p 指定的是同一个内存空间
// 也就是说 wxp 拷贝得到的 wx 的属性和方法
// wx.p 同样也是拥有的
// 调用方式: wx.p.xxxxx 即 wx.p.request({})
const wxp = wx.p = {}
// 3、promisifyAll 作用就是将 wx 内部的方法和属性深拷贝一份给 wxp 这个对象
promisifyAll(wx, wxp)
9.10 调用 Promise 化之后的异步 API
<van-button type="warning" bindtap="getInfo">警告按钮</van-button>
async getInfo () {
const { data: res } = await wx.p.request({
url: 'https://www.escook.cn/api/get',
method: 'GET',
data: {
name: 'zs',
age: 19
}
})
console.log(res)
}
- 此时
wx.p
发起异步的请求时,得到的是一个promise
对象 - 那么我们就可以使用
async/await
简化Promise
语法
// 1、导入 promisifyAll 这个方法
import { promisifyAll } from 'miniprogram-api-promise'
// 2、声明一个空对象 wxp
// wxp 只是一个单纯的变量,只能够在当前 js 中使用,外部不能使用
// wx 是微信小程序最顶级的对象
// 只需要往 wx 上去挂载一个对象即可
// 挂载好,以后 wxp 和 wx.p 指定的是同一个内存空间
// 也就是说 wxp 拷贝得到的 wx 的属性和方法
// wx.p 同样也是拥有的
// 调用方式: wx.p.xxxxx 即 wx.p.request({})
const wxp = wx.p = {}
// 3、promisifyAll 作用就是将 wx 内部的方法和属性深拷贝一份给 wxp 这个对象
promisifyAll(wx, wxp)
如有不足,请多指教,
未完待续,持续更新!
大家一起进步!