微信小程序学习(加深)
一、wx:if 与 hidden 的对比
wx:if
以动态创建和移除元素的方式,控制元素的展示与隐藏
hidden
以切换样式的方式(display: none/block;),控制元素的显示与隐藏
所以需要 频繁切换时,建议使用 hidden
如果条件复杂时,建议使用 wx:if
搭配 wx:elif
、wx:else
进行展示与隐藏的切换,
可以理解为vue
的v-if
和v-show
二、wx:for
通过 wx:for
可以根据指定的数组,循环渲染重复的组件结构:
<view wx:for="{{list}}"> // list即需要循环的数组
索引:{{index}}-当前项:{{item}}
</view>
当前循环项的索引用 index
表示;当前循环项用item
表示
使用 wx:for-index
可以指定当前循环项的索引的变量名
使用 wx:for-item
可以指定当前项的变量名
<view wx:for="{{list}}" wx:for-index="idx" wx:for-item='itm'>
索引:{{idx}}-当前项:{{itm}}
</view>
声明式导航
通过点击<navigator>组件实现页面跳转
如果需要导航到tabBar
页面,我们需要指定好url
地址,以及跳转的方式open-type
表示跳转的方式,必须为 switchTab
,即:
<navigator url="/pages/home/home" open-type="switchTab">跳转tabBar</navigator>
如果导航到非 tabBar
页面,open-type
必须为 navigate
,open-type="navigate"
属性可以省略:
<navigator url="/pages/info/info" open-type="navigate">跳转非 tabBar </navigator>
如果想后退上一页的话,open-type
的值必须是 navigateBack
,表示要进行后退导航,还需要添加delta
其值必须是数字,表示要后退的层级,默认为1:
<navigator open-type="navigateBack" delta='1' >后退</navigator>
编程式导航
调用 wx.switchTab(Object object)
方法进行跳转
如果需要导航到 tabBar
页面,其示例代码:
// 在页面添加点击按钮绑定事件
<button bindtap="gotabBar">跳转tabBar页面</button>
// 当前页面的js文件
gotabBar(){
wx.switchTab({
url: '/pages/home/home',
})
}
如果需要导航到 非tabBar
页面,其示例代码:
// 在页面添加点击按钮绑定事件
<button bindtap="gotabBar">跳转tabBar页面</button>
// 当前页面的js文件
gotabBar(){
wx.navigateTo({
url: '/pages/info/info',
})
}
如果需要后退,可以调用 wx.navigateBack(Object object)
方法,可以返回上一页面或多级页面,主要接收参数delta
,传入Number
类型,默认为1:
// 在页面添加点击按钮绑定事件
<button bindtap="goBack">跳转tabBar页面</button>
// 当前页面的js文件
gotabBar(){
wx.navigateBack()
}
三、生命周期
小程序的生命周期分为两类:
应用生命周期 : 特指小程序从启动 -> 运行 -> 销毁的过程
页面生命周期 : 特指小程序中,每个页面的加载 -> 渲染 -> 销毁的过程
页面的生命周期范围较小,应用程序的生命周期范围较大
了解了生命周期,在每个生命周期就会伴随着生命周期函数:
生命周期函数分为两类,分别是:
应用的生命周期函数 : 特指小程序从启动 -> 运行 -> 销毁期间依次调用的那些函数
页面的生命周期函数 : 特指小程序中,每个页面从加载 -> 渲染 -> 销毁期间依次调用的那些函数
应用的生命周期函数
小程序的应用生命周期函数需要在 app.js
中进行声明:
// app.js
// 主要的函数,分别是:初始化完成的时候,小程序由后台进入,小程序进入后台
App({
onLaunch: function () {},
onShow: function () { },
onHide: function () { }
});
页面的生命周期函数
小程序的页面生命周期函数需要在页面的 .js 文件中进行声明:
Page({
/**
* 页面的初始数据
*/
data: {},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {}
})
四、WXS 脚本
WXS
(WeiXin Script)是小程序独有的一套脚本语言,结合 WXML
,可以构建出页面的结构,可以理解为一种写在外面在引入使用的js代码,wxml
中无法调用在页面的 .js 中定义的函数,所以一般是作为”过滤器”来使用的。
使用方法:
在项目新建一个util
的文件夹,用于汇总文件,新建一个index.wxs
的文件,在里面编写一些函数导出:
function toLower(str) {
return str.toLowerCase()
}
module.exports = {
toLower: toLower
}
在需要使用这个函数的wxml
的文件里面再最后面添加<wxs></wxs>标签:
// 页面使用函数
<view>{{m2.toLower(count2)}}</view>
// module 用来指定模块的名称 src 用来指定要引入的脚本的路径,且必须是相对路径
<wxs module="m2" src="../../util/index.wxs"></wxs>
如果想直接写在wxml
文件,可以直接在下方书写即可:
<view>{{m1.toUpper(count)}}</view>
<wxs module="m1">
module.exports.toUpper = function (str) {
return str.toUpperCase()
}
</wxs>
五、数据监听器
数据监听器用于监听和响应任何属性和数据字段的变化,从而执行特定的操作。它的作用类似于 vue
中的 watch
侦听器。在小程序组件中,数据监听器的基本语法格式如下:
Component({
observers:{
'监听数据1,监听数据2':function(新数据1,新数据2){
// 这里书写处理内容
}
}
})
如果某个对象中需要被监听的属性太多,为了方便,可以使用通配符 **
来监听对象中所有属性的变化:
Component({
observers:{
'data.**':function(obj){
this.setData({
newData:`${obj.a},${obj.b}`
})
}
}
})
六、组件
组件的生命周期函数
在小程序组件中,最重要的生命周期函数有 3 个,分别是 created
、attached
、detached
,主要为:
组件实例刚被创建好的时候,
created
生命周期函数会被触发在组件完全初始化完毕、进入页面节点树后,
attached
生命周期函数会被触发在组件离开页面节点树后,
detached
生命周期函数会被触发
其他的周期函数:
ready
:在组件在视图层布局完成后执行moved
: 在组件实例被移动到节点树另一个位置时执行error
: 每当组件方法抛出错误时执行,会获得一个err的参数
lifetimes 节点
在小程序组件中,生命周期函数可以直接定义在 Component
构造器的第一级参数中,可以在lifetimes
字段 内进行声明(这是推荐的方式,其优先级最高)
Component({
// 推荐写法
lifetimes: {
attached() { },
detached() { }
},
// 旧式写法
attached() { },
detached() { }
})
组件所在页面的生命周期
组件所在页面的生命周期函数有如下 3 个,分别是:
show
: 组件所在的页面被展示时执行hide
: 组件所在的页面被隐藏时执行resize
: 组件所在的页面尺寸变化时执行,接收size参数
pageLifetimes 节点
组件所在页面的生命周期函数,需要定义在 pageLifetimes
节点中,示例代码如下:
Component({
pageLifetimes: {
show: function () { }, // 页面被显示
hide: function () { }, // 页面被隐藏
resize: function (size) { }, // 页面尺寸发生变化
}
})
七、插槽
了解vue
对于插槽肯定不陌生,对于小程序,侧重于启用多个插槽,在小程序的自定义组件中,需要使用多 插槽时,可以在组件的 .js 文件中,通过如下方式进行启用。
Component({
options: {
multipleSlots: true // 启用多个slot
}
})
在组件的 .wxml
中使用多个 标签,以不同的 name
来区分不同的插槽:
<view>
<slot name='slotOne'></slot>
<view>------</view>
<slot name='slotTwo'></slot>
</view>
使用的时候需要注明slot
属性:
<!-- 使用 -->
<my-info>
<view slot='slotOne'>这是slotOne</view>
<view slot='slotTwo'>这是slotTwo</view>
</my-info>
八、父子组件之间的通信
- 属性绑定用于实现父向子传值,而且只能传递普通类型的数据,无法将方法传递给子组件:
// 父组件定义数据
data: {
num: 0
}
// 向组件传值
<my-info num='{{num}}'></my-info>
// 子组件接收
properties: {
num: Number
}
// 子组件使用
<text>num:{{num}}</text>
- 事件绑定用于实现子向父传值,可以传递任何类型的数据:
// 子组件的wxml
<button type="primary" bindtap="addNum">+1</button>
// 子组件的js绑定事件
methods: {
addNum() {
// 修改自身的num
this.setData({
num: this.properties.num + 1
})
this.triggerEvent('add', { value: this.properties.num })
}
}
// 父组件的wxml
<my-info bindadd="changeNum"></my-info>
// 父组件的js
changeNum(e) {
this.setData({
num: e.detail.value // value是传入的值
})
}
- 获取组件实例:可在父组件里调用
this.selectComponent("id或class选择器")
,获取子组件的实例对象,从而直接访问子组 件的任意数据和方法。调用时需要传入一个选择器:
// 父组件的wxml
<my-info class="info"></my-info>
<button bindtap="getInfo">获取组件实例</button>
// 父组件的js
getInfo() {
const myInfo = this.selectComponent('.info')
myInfo.setData({ num: myInfo.properties.num + 1 }) // 调用组件的方法
myInfo.addNum() // 调用组件的方法
}
九、behaviors
behaviors
是小程序中,用于实现组件间代码共享的特性,类似于 Vue.js
中的 mixins
。
在项目新建文件夹behaviors
,用于汇总,在该文件夹下创建index.js
文件,调用 Behavior(Object object)
方法即可创建一个共享的 behavior
实例对象,供所有的组件使用:
module.exports = Behavior({
properties: {},
data: { name: 'zs' },
methods: {}
})
需要使用到该文件的内容可以引入即可:
const myBehavior = require('../behaviors/index')
Component({
behaviors: [myBehavior],
...
})
十、npm 包
在小程序也是可以使用一些npm包来加速项目的搭建
Vant Weapp
Vant Weapp
是有赞前端团队开源的一套小程序 UI 组件库,助力开发者快速搭建小程序应用
官网地址:<Vant Weapp)>
使用步骤:
输入:
// 先初始化包,这里需要安装node
npm init -y
// 安装Vant Weapp,如果安装不了需要管理员身份运行,或者看清楚安装的路径是否是项目的路径
npm i @vant/weapp@1.3.3 -S --production
我们还需要构建npm:
如果出现报错:
我们需要找到project.config.json
文件,修改一些内容:
{
...
"setting": {
...
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram/"
}
]
}
}
构建好了项目会出现miniprogram_npm
文件夹
安装构建完 Vant
组件库之后,将 app.json
中的 "style": "v2"
去除,在 app.json
的 usingComponents
节点中引入需要的组件:
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
使用:在需要用到按钮的wxml文件测试一下:
<van-button type='primary'>按钮</van-button>
页面呈现了绿色的按钮即代表引入成功了
如果想定制全局主题样式,可以在 app.wxss 中,写入 CSS 变量,即可对全局生效:
page{
--button-primary-background-color:red
}
这样我们再去看,按钮就会变成红的,想自己定义一些样式,可以去<vant-weapp/var.less at dev · youzan/vant-weapp (github.com)>了解更多样式的修改
十一、API Promise化
API Promise
化,指的是通过额外的配置,将官方提供的、基于回调函数的异步 API,升级改造为基于 Promise
的异步 API,从而提高代码的可读性、维护性,避免回调地狱的问题。
我们需要安装相应的包:
npm i miniprogram-api-promise@1.0.4 --save
安转好之后再次构建npm
,在app.js顶部添加代码:
import { promisifyAll } from 'miniprogram-api-promise'
const wxp = wx.p = {}
promisifyAll(wx, wxp)
使用:
// wxml结构
<van-button type='primary' bindtap="getInfo">按钮</van-button>
//js结构
async getInfo() {
const res = await wx.p.request({
method: 'GET请求方式',
uri: '请求地址',
data: { 请求数据 }
})
console.log(res)
}
十二、全局数据共享
全局数据共享是为了解决组件之间数据共享的问题,可以理解为vue
的vuex
全局数据共享,
在小程序中,可使用mobx-miniprogram
配合 mobx-miniprogram-bindings
实现全局数据共享
安装 MobX
相关的包:
npm i mobx-miniprogram@4.13.2 --save
npm i mobx-miniprogram-bindings@1.2.1 --save
再次构建npm
我们需要在项目创建文件夹store
,在该文件夹下创建store.js
文件,创建 MobX
的 Store
实例:
import { observable, action } from 'mobx-miniprogram'
export const store = observable({
// 定义数据
num1: 1,
num2: 2,
// 计算,类似于getter
get sum() {
return this.num1 + this.num2
},
// action方法
changeNum1: action(function (value) {
this.num1 += value
})
})
将 Store 中的成员绑定到页面中
// 在我们需要使用store的js文件引入即可
import { createStoreBindings } from 'mobx-miniprogram-bindings'
import { store } from '../../store/store'
Page({
...
onLoad: function () {
this.storeBindings = createStoreBindings(this, {
store,
fields: ['num1', 'num2', 'sum'],
actions: ['changeNum1']
})
},
onUnload: function () {
this.storeBindings.destoryStoreBindings()
}
})
页面的展示:
// wxml页面
<view>{{num1}}+{{num2}}={{sum}}</view>
<van-button type='primary' bindtap="addNum1" data-value="{{1}}">num1+1</van-button>
//js页面绑定函数
addNum1(e) {
this.changeNum1(e.target.dataset.value)
}
当我们点击按钮的时候就会触发addNum1函数,该函数会去触发store.js的changeNum1,将num1的数值进行加一,从而引起sum的值的变化。
将 Store 中的成员绑定到组件中
在组件中的绑定和页面有一点点区别:
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from '../store/store'
Component({
behaviors: [storeBindingsBehavior],
storeBindings: {
store,
fields: {
num1: () => store.num1, // 绑定的第一种方法
num2: (store) => store.num2, // 绑定的第二种方法
sum: 'sum'
},
actions: {
changeNum1: 'changeNum1'
}
}
})
使用方法和页面的使用方法一致:
// wxml页面
<view>{{num1}}+{{num2}}={{sum}}</view>
<van-button type='primary' bindtap="addNum1" data-value="{{1}}">num1+1</van-button>
//js页面绑定函数
addNum1(e) {
this.changeNum1(e.target.dataset.value)
}
十三、分包
分包指的是把一个完整的小程序项目,按照需求划分为不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。
配置方法:
在app.json
的文件中,pages
选项是我们主包的保存路径,所有的tabBar
只能写在这里进行主要加载,其他的部分可以写在subpackages
选项下进行分包加载,小程序会按 subpackages
的配置进行分包,subpackages
之外的目录将被打包到主包中。
例如:
"subPackages": [
{
"root": "packageA", // 第一个分包的目录名称
"pages": [
"pages/cart/cart", // 包的存放路径
"pages/myShop/myShop"
]
},
{
"root": "packageB",
"pages": [
"pages/cart2/cart2",
"pages/myShop2/myShop2"
]
}
]
如果需要设置成独立分包,即不依赖主包即可运行,这样可以很大程度上提升分包页面的启动速度,普通分包必须依赖于主包才能运行 ,而独立分包可以在不下载主包的情况下,独立运行
我们只需要在普通分包添加independent
即可,以packageB
为例:
{
"root": "packageB",
"pages": [
"pages/cart2/cart2",
"pages/myShop2/myShop2"
],
"independent": true
}
分包预下载
在进入小程序的某个页面时,由框架自动预下载可能需要的分包,从而提升进入后续分包 页面时的启动速度。
在项目的app.json
文件添加代码:
// 该属性与pages平级
"preloadRule": {
// 当我们进入到pages/home/home路径下就开始预下载packageA这个包
"pages/home/home":{ // 触发分包预下载的路径
"network": "all", // 默认为WiFi
"packages": ["packageA"] // 预下载的包,可以通过root或者name来指定预下载的包
}
}
十四、自定义 tabBar
小程序官网自定义tabBar
的地址:<自定义 tabBar | 微信开放文档 (qq.com)>
在该地址的添加 tabBar
代码文件可以引入我们的vant组件库
:<Tabbar 标签栏 - Vant Weapp )>
使用步骤:
- 在
app.json
中的tabBar
项指定custom
字段,同时其余tabBar
相关配置也补充完整。 - 新建
custom-tab-bar
文件夹,在该文件夹创建index.js、index.json、index.wxml、index.wxss - 复制
vant
组件库的自定义图标
定义custom-tab-bar
下的index.wxml
文件夹:
<van-tabbar active-color='#13A7A0' active="{{ active }}" bind:change="onChange">
<van-tabbar-item info='{{item.info?item.info:""}}' wx:for="{{list}}" wx:key="index">
<image slot="icon" src="{{item.iconPath}}" mode="aspectFit" style="width: 25px; height: 25px;" />
<image slot="icon-active" src="{{item.selectedIconPath}}" mode="aspectFit" style="width: 25px; height: 25px;" />
{{item.text}}
</van-tabbar-item>
</van-tabbar>
定义custom-tab-bar
下的index.js
文件夹:
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from '../store/store'
Component({
options: {
styleIsolation: 'shared'
},
behaviors: [storeBindingsBehavior],
storeBindings: {
store,
fields: {
active: 'active'
},
actions: {
updateActive: 'updateActive'
}
},
data: {
"list": [
{
"pagePath": "/pages/home/home", // 跳转路径
"text": "首页",
"iconPath": "/images/...", // 未选中的图标
"selectedIconPath": "/images/..." // 选中的图标
},
{
"pagePath": "/pages/cart/cart",
"text": "购物车",
"iconPath": "/images/...",
"selectedIconPath": "/images/...",
"info": 2
},
{
"pagePath": "/pages/my/my",
"text": "我们",
"iconPath": "/images/...",
"selectedIconPath": "/images/..."
}
]
},
methods: {
onChange(event) {
this.updateActive(event.detail)
wx.switchTab({
url: this.data.list[event.detail].pagePath,
})
},
}
})
在index.wxss
文件修改一定的样式:
.van-tabbar-item{
--tabbar-item-margin-bottom:0;
}
store
文件代码:
import { observable, action } from 'mobx-miniprogram'
export const store = observable({
active:0,
get sum() {},
updateActive: action(function (index) {
this.active = index
})
})