微信小程序学习笔记

一、简介

1、组件

  • 视图容器
  1. view
    • 普通视图区域
    • 类似于 HTML 中的 div,是一个块级元素
    • 常用来实现页面的布局效果
  2. scroll-view
    • 可滚动的视图区域
    • 常用来实现滚动列表的效果
  3. swiper 和 swiper-item
    • 轮播图容器组件和轮播图 item 组件
  • 基础内容
  • 表单组件
  • 导航组件
  • 媒体组件
  • map 地图组件
  • canvas 画布组件
  • 开放能力
  • 无障碍访问

2、API

  • 事件监听 API
    • 特点:以 on 开头,用来监听某些事件的触发
    • 举例:wx.onWindowResize(function callback) 监听窗口尺寸变化的事件
  • 同步 API
    • 特点1:以 Sync 结尾的 API 都是同步 API
    • 特点2:同步 API 的执行结果。可以通过函数返回值直接获取,如果执行出错会抛出异常
    • 举例: wx.setStorageSync('key', 'value') 向本地存储中写入内容
  • 异步 API
    • 特点:类似于 jQuery 中的 $.ajax(option) 函数,需要通过 success、fail、complete 接收回调的结果
    • 举例:wx.request() 发起网络数据请求,通过 success 回调函数接收数据

二、WXML 模板语法

1、数据绑定

  • 数据绑定的基本原则

    1. 在 data 中定义数据

    2. 在 WXML 中使用数据

    3. 把 data 中的数据绑定到页面中渲染,使用 Mustache 语法(双大括号) 将变量包起来

      <view>{{要绑定的数据名称}}</view>

  • Mustache 语法的使用场景

    1. 绑定内容
    2. 绑定属性

    demo.js

    Page({
    	data: {
    		src: 'www.baidu.com'
    	}
    })
    

    demo.wxml

    <image src="{{ src }}"></image>
    
    1. 运算(三元运算、算术运算等)

    demo.js

    Page({
    	data: {
    		randomNum: Math.random()*10 // 生成 10 以内的随机数
    	}
    })
    

    deom.wxml

    <view>{{ randomNum >= 5 ? '随机数字大于或等于5' : '随机数小于5' }}</view>
    

2、事件绑定

事件是渲染层到逻辑层的通讯方式。通过事件可以将用户在渲染层的行为,反馈到逻辑层进行业务处理。

image-20220817223407493

2.2.1 小程序中常用的事件

类型绑定方式事件描述
tapbindtap 或 band:tap手指触摸后马上离开,类似于 HTML 中的
inputbindinput 或 bind:input文本框的输入事件
changebindchange 或 bind:change状态改变时触发

2.2.2 事件对象的属性列表

当事件回调触发的时候,会收到一个事件对象 event,它的详细属性如下所示

事件属性

2.2.3 target 和 currentTarget 的区别

target 是触发该事件的源头组件,而 currentTarget 则是当前事件所绑定的组件。

点击内部的按钮,点击事件以冒泡的方式向外扩散,也会触发外层 view 的 tap 事件处理函数。

此时,对于外层的 view 来说:

  • e.target 指向的是触发事件得到源头组件,因此,e.target 是内部的按钮组件
  • e.currentTarget 指向的是当前正在触发事件的那个组件,因此,e.currentTarget 是当前的 view 组件

2.2.4 bindtap 的语法格式

在小程序中,不存在 HTML 中的 onclick 鼠标点击事件,而是通过 tap 事件来响应用户的触摸行为

  1. 通过 bindtap,可以为组件绑定 tap 触摸事件

    <button type="primary" bindtap="btnHandle">按钮</button>
    
  2. 在页面的 .js 文件中定义对应的事件处理函数,事件参数通过形参 event (一般简写成 e)来接受

    Page({
        // 定义按钮的事件处理函数
        btnHandle(e) {
            // 事件对象 event
            console.log(e);
            // 获取自定义属性(参数)
            console.log(e.target.dataset.val);
    	}
    })
    

2.2.5 在事件处理函数中为 data 中的数据赋值

通过调用 this.setData(dataObject) 方法,可以给页面 data 中的数据重新赋值

Page({
    data: {
        count: 0
    }
    // 修改 count 的值
    changeCount() {
    	this.setData({
            count: this.data.count + 1
        })
	}
})

2.2.6 事件传参

可以为组件提供 data-* 自定义属性传参,其中 * 代表的是参数的名字

如果不使用 Mustache 语法(双大括号),参数 2 会被解析为字符串格式
<button type="primary" bindtap="btnHandle" data-val="{{2}}">事件传参</button>
  • val 会被解析为参数的名字
  • 数值 2 会被解析为参数的值

在事件处理函数中,通过 event.target.dataset.参数名 来获取到具体参数的值

btnHandle(e) {
    // 获取自定义属性(参数)val
    console.log(e.target.dataset.val);
}

2.2.7 bindinput 的语法格式

在小程序中,通过 input 事件来响应文本框的输入事件

  1. 通过 bindinput,可以为文本框绑定输入事件

    <input bindinput="inputHandle" type="text"/>
    
  2. 在页面的 .js 文件中定义对应的事件处理函数

    inputHandle(e) {
        // 通过 e.detail.value 获取文本框最新的值,是变化过后的值
        console.log(e.detail.value);
    }
    

2.2.8 实现文本框和 data 之间的数据同步(双向数据绑定)

实现思路:

  1. 先将文本框的内容和 data 的 msg 进行动态绑定
  2. 监听文本框输入事件,通过 this.setData() 将文本框的最新值(event.detail.value)赋给 msg

具体步骤:

  1. 定义数据

    Page({
        data: {
            msg: '你好'
        }
    })
    
  2. 渲染结构

    <input value="{{msg}}" bindinput="inputHandle" type="text"/>
    
  3. 绑定 input 事件处理函数

    inputHandle(e) {
        this.setData({
            // 通过 e.detail.value 获取文本框最新的值
            msg: e.detail.value	
        })
    }
    

3、条件渲染

2.3.1 wx:if

在小程序中,使用 wx:if="{{ condition }}" 来判断是否需要渲染该代码块

<view wx:if="{{ condition }}">True</view>

也可以使用 wx:elifwx:else 来添加判断

<view wx:if="{{ type === 1 }}"></view>
<view wx:elif="{{ type === 2 }}"></view>
<view wx:else>保密</view>

2.3.2 结合 <block> 标签使用 wx:if

如果要一次性控制多个组件的展示与隐藏,可以使用一个 <block></block> 标签将多个组件包装起来,并在 <block> 标签上使用 wx:if 控制属性。<block> 标签只起包裹作用,不参与页面渲染,相当于 vue 中的 <template>

<block wx:if="{{true}}">
	<view>view1</view>
    <view>view2</view>
</block>

2.3.3 hidden

在小程序中,直接使用 hidden="{{ condition }}" 也能控制元素的现实与隐藏

<view hidden="{{ condition }}">条件为 true 隐藏,条件为 false 显示</view>

2.3.4 wx:if 与 hidden 的对比

  1. 运行方式不同
    • wx:if 以动态创建和移除元素的方式,控制元素的展示与隐藏
    • hidden 以切换样式的方式(display: none/block;),控制元素的现实与隐藏
  2. 使用建议
    • 频繁切换是,建议使用 hidden
    • 控制条件复杂时,建议使用 wx:if 搭配 wx:elifwx:else 进行展示与隐藏的切换

4、列表渲染

2.4.1 wx:for

通过 wx:for 可以根据指定的数组,循环渲染重复的组件结构

默认情况下,当前循环项的索引index 表示;当前循环项item 表示

wx:key="index" 绑定key值。不加 {{}}

<view wx:for="{{arr}}" wx:key="index">
	当前项的索引:{{index}}, 当前项:{{item}}
</view>

三、WXSS 模板样式

1、WXSS 和 CSS 的关系

WXSS 具有 CSS 大部分特征,同时,WXSS 还对 CSS 进行了扩充以及修改,以适应微信小程序的开发,与 CSS 相比,WXSS 扩展的特征有:

  • rpx 尺寸单位

    rpx(responsive pixel)是微信小程序独有的,用来解决屏幕适配的尺寸单位

    实现原理:

    鉴于不同设备屏幕的大小不同,为了实现屏幕的自动适配,rpx 把所有设备的屏幕在宽度上等分为 750 份(即:当前屏幕的总宽度为 750 rpx

    • 在较小的设备上,1 rpx 所代表的宽度较小
    • 在较大的设备上,1 rpx 所代表的宽度较大

    小程序在不同设备上运行的时候,会自动把 rpx 的样式单位换算成对应的像素单位来渲染,从而实现屏幕适配。

    官方建议:开发微信小程序时,设计师可以使用 iPhone6 作为视觉稿的标准(1px 正好等于 2rpx)。

    开发举例:在 iPhone6 上如果要绘制宽 100px,高 20px 的盒子,换算成 rpx 单位,宽高分别为 200rpx 和 40rpx。

  • @import 样式导入

    @import 后跟需要导入的外联样式表的相对路径,用 ; 表示语句结束。

    /** common.wxss **/
    body {
        margin: 0;
        padding: 0;
    }
    
    @import "common.wxss";
    .box1 {
        padding: 10px;
    }
    

2、全局样式和局部样式

  • app.wxss 文件中定义的样式为全局样式,作用于每一个页面。
  • 在页面的 .wxss 文件中定义的样式为局部样式,只作用于当前页面。

注意:

  • 当局部样式和全局样式冲突时,根据就近原则,局部样式会覆盖全局样式。
  • 当局部样式的权重大于或等于全局样式的权重时,才会覆盖全局的样式。

四、全局配置

1、 全局配置文件及常用的配置项

小程序根目录下的 app.json 文件时小程序的全局配置文件。常用的配置项如下:

  1. pages
    • 记录当前小程序所有页面的存放路径
  2. window
    • 全局配置小程序窗口的外观
  3. tabBar
    • 设置小程序底部的 tabBar 效果
  4. style
    • 是否启用新版的组件样式

2、 window

4.2.1 小程序窗口的组成部分

4.2.2 window 节点常用的配置项

设置导航栏的标题

设置步骤:app.json -> window -> navigationBarTitleText

设置导航栏的背景色

设置步骤:app.json -> window -> navigationBarBackgroundColor

设置导航栏的标题颜色

设置步骤:app.json -> window -> navigationBarTextStyle

全局开启下拉刷新功能

概念:下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。

设置步骤:app.json -> window -> 把 enablePullDownRefresh 的值设置为 true(在 app.json 中开启下拉刷新会作用于每一个页面)

设置下拉刷新时窗口的背景色

当全局开启下拉刷新功能之后,默认窗口背景颜色为白色。

设置步骤:app.json -> window -> 为 backgroundColor 指定 16 进制的颜色值(如:#efefef)

设置下拉刷新时 loading 的样式

当全局开启下拉刷新功能之后,默认窗口的 loading 样式为白色。

设置步骤:app.json -> window -> 为 backgroundTextStyle 指定 dark 值。(可选值只有 dark 和 light)

设置上拉触底的距离

概念:上拉触底是移动端的专有名词,指的是通过手指在屏幕上的上拉滑动操作,从而加载更多数据的行为。

设置步骤:app.json -> window -> 把 onReachBottomDistance 设置新的数值

示例

"window": {
    "navigationBarBackgroundColor": "#2b4b6b",
    "navigationBarTitleText": "第一个小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true,
    "backgroundColor": "#999",
    "backgroundTextStyle": "dark",
    "onReachBottomDistance": 50
}

3、 tabBar

4.3.1 什么是 tabBar

tabBar 是移动端应用常见的页面效果,用于实现多页面的快速切换。小程序通常将其分为:

  • 底部 tabBar

  • 顶部 tabBar

注意:

  • tabBar 中只能配置最少 2 个、最多 5 个 tab 页签
  • 当渲染顶部 tabBar 时,不显示 icon,只显示文本

4.3.2 tabBar 的六个组成部分

  1. backgroundColor:tabBar 的背景色
  2. selectedIconPath:选中时的图片路径
  3. borderStyle:tabBar 上边框的颜色
  4. iconPath:未选中时的图片路径
  5. selectedColor:tab 上的文字选中时的颜色
  6. color:tab 上文字的默认(未选中)颜色

4.3.3 tabBar 节点的配置项

4.3.4 每个 tab 项的配置选项

示例

"pages": [
    "pages/demo01/demo01",
    "pages/list/list",
],

"tabBar": {
    "selectedColor": "#C00000", // tab 上的文字选中时的颜色
    "list": [
        {
            "pagePath": "pages/demo01/demo01", // 页面路径,必须在 pages 中预先定义
            "text": "demo01" // tab 上显示的文字
            "iconPath": "图片路径", // 未选中时的图标路径
        	"selectedIconPath": "图片路径" // 选中时的图标路径
        },
        {
            "pagePath": "pages/list/list",
            "text": "demo02"
            "iconPath": "图片路径",
        	"selectedIconPath": "图片路径"
        }
    ]
}

五、网络数据请求

1、小程序中网络数据请求的限制

出于安全性方面的考虑,小程序官方对数据接口的请求做出了如下两个限制:

  1. 只能请求 HTTPS 类型的接口
  2. 必须将接口的域名添加到信任列表

2、配置 request 合法域名

配置步骤:

登录微信小程序管理后台 -> 开发 -> 开发设置 -> 服务器域名 -> 修改 request 合法域名


注意事项

  1. 域名只支持 https 协议
  2. 域名不能使用 IP 地址或 localhost
  3. 域名必须经过 ICP 备案
  4. 服务器域名一个月最多可申请 50 次修改

3、发起 GET 请求

调用微信小程序提供的 wx.request() 方法,可以发起 GET 数据请求

wx.request({
    // 请求的接口地址,必须基于 https 协议
    url: 'https://www.escook.cn/api/get',
    // 请求方式
    method: 'GET',
    // 带给服务器的数据
    data: {
        name: 'andy',
        age: 18
    },
    // 请求成功后的回调
    success: res => {
        console.log(res);
    }
})

4、发起 POST 请求

调用微信小程序提供的 wx.request() 方法,可以发起 POST 数据请求(配置 method 为 POST)

wx.request({
    // 请求的接口地址,必须基于 https 协议
    url: 'https://www.escook.cn/api/get',
    // 请求方式
    method: 'POST',
    // 带给服务器的数据
    data: {
        name: 'jack',
        age: 20
    },
    // 请求成功后的回调
    success: res => {
        console.log(res);
    }
})

5、在页面刚加载时请求数据

在页面刚加载的时候,自动请求一些初始化的数据。此时需要在页面的 onLoad 事件中调用获取数据的函数

/**
	* 生命周期函数--监听页面加载(页面一加载就触发这个函数)
*/
onLoad(options) {
    this.getInfo()
},

// 发送 get 请求
getInfo() {
    wx.request({
        url: 'https://www.escook.cn/api/get',
        method: 'GET',
        data: {
            name: 'andy',
            age: 18
        },
        success: res => {
            console.log(res);
        }
    })
},

6、跳过 request 合法域名校验

在开发过程中,如果后端程序员仅仅提供了 http 协议的接口、暂时没有提供 https 协议的接口,此时,为了不耽误开发的进度,我们可以在微信开发者工具中,临时开启【不校验请求域名、web-view(业务域名)、TLS 版本及 HTTPS 证书】选项,跳过 request 合法域名的校验

注意:跳过 request 合法域名校验的选项,仅限在开发与调试阶段使用!

7、关于跨域和 Ajax 的说明

跨域问题只存在于基于浏览器的 Web 开发中。由于小程序的宿主环境不是浏览器,而是微信客户端,所以小程序中不存在跨域的问题。

Ajax 技术的核心是依赖于浏览器中的 XMLHttpRequest 这个对象,由于小程序的宿主环境是微信客户端,所以小程序不能叫做“发起 Ajax 请求”,而是叫做“发起网络数据请求”。


六、页面导航

6.1 什么是页面导航

页面导航指的是页面之间的相互跳转。例如,浏览器中实现页面导航的方式有如下两种:

  • <a> 链接
  • location.href

6.2 小程序中实现页面导航的两种方式

6.2.1 声明式导航

在页面上声明一个 <navigator> 导航组件

通过点击 <navigator> 组件实现页面跳转

  1. 导航到 tabBar 页面

    tabBar 页面指的是被配置为 tabBar 的页面。在使用 <navigator> 组件跳转到指定的 tabBar 页面时,需要指定 url 属性和 open-type 属性,其中:

    • url 表示要跳转的页面的地址,必须以 / 开头

    • open-type 表示跳转得到方式,必须为 switchTab

      <navigator url="/pages/message/message" open-type="switchTab">消息页面</navigator>
      
  2. 导航到非 tabBar 页面

    非 tabBar 页面指的是没有被配置为 tabBar 的页面。在使用 <navigator> 组件跳转到普通的非 tabBar 页面时,需要指定 url 属性和 open-type 属性,其中:

    • url 表示要跳转的页面的地址,必须以 / 开头

    • open-type 表示跳转得到方式,必须为 navigate

      <navigator url="/pages/index/index" open-type="navigate">普通页面</navigator>
      

    注意:为了简便在导航到非 tabBar 页面时,open-type="navigate" 属性可以省略

  3. 后退导航

    如果要后退到上一页面或多级页面,则需要指定 open-type 属性和 delta 属性,其中:

    • open-type 的值必须是 navigateBack,表示要进行后退导航

    • delta 的值必须是数字,表示要后退的层级

      <navigator open-type="navigateBack" delta="1">后退导航</navigator>
      

    注意:为了简便,如果只是后退到上一页面,则可以省略 delta 属性,因为其默认值就是 1

6.2.2 编程式导航

调用小程序的导航 API,实现页面的跳转

  1. 导航到 tabBar 页面

    调用 wx.switchTab(Object object) 方法,可以跳转到 tabBar 页面。其中 Object 参数对象的属性列表如下:

    // 页面结构
    <button bindtap="goMessage">编程式导航</button>
    
    // 通过编程式导航,跳转到 message 页面
    goMessage() {
        wx.switchTab({
            url: '/pages/message/message',
        })
    }
    
  2. 导航到非 tabBar 页面

    调用 wx.navigateTo(Object object) 方法,可以跳转到非 tabBar 页面。其中 Object 参数对象的属性列表如下:

    // 页面结构
    <button bindtap="goInfo">编程式导航</button>
    
    // 通过编程式导航,跳转到 info 页面
    goMessage() {
        wx.navigateTo({
            url: '/pages/info/info',
        })
    }
    
  3. 后退导航

    调用 wx.navigateBack(Object object) 方法,可以返回上一页面或多级页面。其中 Object 参数对象可选的属性列表如下:

    // 页面结构
    <button bindtap="goBack">编程式导航</button>
    
    // 通过编程式导航,跳转到 info 页面
    goMessage() {
        wx.navigateBack()
    }
    

    注意:为了简便,如果只是后退到上一页面,则可以省略 delta 属性,因为其默认值就是 1

6.3 导航传参

6.3.1 声明式导航传参

navigate 组件的 url 属性用来指定将要跳转到的页面的路径。同时,路径的后面还可以携带参数:

  • 参数与路径之间使用 ? 分隔
  • 参数键与参数值用 = 相连
  • 不同参数用 & 分隔
<navigator url="/pages/message/message?name=zs&age=20">消息页面</navigator>

6.3.2 编程式导航传参

调用 wx.navigateTo(Object object) 方法跳转页面时,也可以携带参数

// 页面结构
<button bindtap="goInfo">编程式导航</button>

// 通过编程式导航,跳转到 info 页面
goMessage() {
    wx.navigateTo({
        url: '/pages/info/info?name=zs&age=20',
    })
}

6.3.3 在 onLoad 中接收导航参数

通过声明式导航传参或编程式导航传参所携带的参数,可以直接在 onLoad 事件中直接获取到

/**
* 生命周期函数--监听页面加载(页面一加载就触发这个函数)
*/
onLoad(options) {
    // options 就是导航传递过来的参数对象
    console.log(options)
}

七、页面事件

7.1 下拉刷新事件

7.1.1 什么是下拉刷新

下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。

7.1.2 启用下拉刷新

启用下拉刷新的两种方式:

  1. 全局开启下拉刷新

    app.json 的 window 节点中,把 enablePullDownRefresh 的值设置为 true

    "window": {
        "enablePullDownRefresh": true,
    }
    
  2. 局部开启下拉刷新

    在页面的 .json 配置文件中,把 enablePullDownRefresh 的值设置为 true

    {
    	"usingComponents": {},
    	"enablePullDownRefresh": true
    }
    

在实际开发中,推荐使用第二种方式,为需要的页面单独开启下拉刷新的效果

7.1.3 监听页面的下拉刷新事件

在页面的 .js 文件中,通过 onPullDownRefresh() 函数即可监听当前页面的下拉刷新事件

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
    console.log('触发了下拉刷新事件');
}

7.1.4 停止下拉刷新的效果

当处理完下拉刷新后,下拉刷新的 loading 效果会一直显示,不会主动消失,所以需要手动隐藏 loading 效果。此时,调用 wx.stopPullDownRefresh() 可以停止当前页面的下拉刷新

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
    console.log('触发了下拉刷新事件');
    // 当下拉刷新操作成功后,调用此函数,关闭下拉刷新效果
    wx.stopPullDownRefresh()
}

八、生命周期

8.1 什么是生命周期

生命周期(LIfe Cycle)是指一个对象从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。

我们可以把每个小程序运行的过程,也概括为生命周期:

  • 小程序的启动,表示生命周期的开始
  • 小程序的关闭,表示生命周期的结束
  • 中间小程序运行的过程,就是小程序的生命周期

8.2 生命周期的分类

在小程序中,生命周期分为两类,分别是:

  1. 应用生命周期
    • 特指小程序从启动 -> 运行 -> 销毁的过程
  2. 页面生命周期
    • 特指小程序中,每个页面的加载 -> 渲染 -> 销毁的过程

其中,页面的生命周期范围较小,应用程序的生命周期范围较大,如图所示:

8.3 什么是生命周期函数

  • 生命周期函数

    是由小程序框架提供的内置函数,会伴随着生命周期,自动按次序执行。

  • 生命周期函数的作用

    允许程序员在特定的时间点,执行某些特定的操作。例如,页面刚加载的时候,可以在 onLoad 生命周期函数中初始化页面的数据。

注意:生命周期强调的是时间段,生命周期函数强调的是时间点。

8.4 生命周期函数的分类

在小程序中,生命周期函数分为两类,分别是:

  1. 应用的生命周期函数
    • 特指小程序从启动 -> 运行 -> 销毁期间依次调用的那些函数
  2. 页面的生命周期函数
    • 特指小程序中,每个页面的加载 -> 渲染 -> 销毁期间依次调用的那些函数

8.5 应用的生命周期函数

小程序的应用生命周期函数需要在 app.js 中进行声明

// app.js
App({

	/**
	 * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
	 */
	onLaunch: function () {},

	/**
	 * 当小程序启动,或从后台进入前台显示,会触发 onShow
	 */
	onShow: function (options) {},

	/**
	 * 当小程序从前台进入后台,会触发 onHide
	 */
	onHide: function () {},

	/**
	 * 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
	 */
	onError: function (msg) {}
})

8.6 页面的生命周期函数

小程序的页面生命周期函数需要在页面的 .js 中进行声明

// pages/home/home.js
Page({	
	/**
	 * 生命周期函数--监听页面加载
	 */
	onLoad(options) {},

	/**
	 * 生命周期函数--监听页面初次渲染完成
	 */
	onReady() {},

	/**
	 * 生命周期函数--监听页面显示
	 */
	onShow() {},

	/**
	 * 生命周期函数--监听页面隐藏
	 */
	onHide() {},

	/**
	 * 生命周期函数--监听页面卸载
	 */
	onUnload() {}
})
  • onLoad(Object query)

    页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。

    参数:

    名称类型说明
    queryObject打开当前页面路径中的参数
  • onShow()

    页面显示/切入前台时触发。

  • onReady()

    页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。

    注意:对界面内容进行设置的 API 如 wx.setNavigationBarTitle(修改页面标题),请在onReady之后进行。详见生命周期

  • onHide()

    页面隐藏/切入后台时触发。 如 wx.navigateTo 或底部 tab 切换到其他页面,小程序切入后台等。

  • onUnload()

    页面卸载时触发。如 wx.redirectTowx.navigateBack 到其他页面时。


九、wxs 脚本

9.1 概念

9.1.1 什么是 wxs

WXS(WeiXin Script) 是小程序独有的一套脚本语言,结合 wxml,可以构建出页面的结构

9.1.2 wxs 的应用场景

wxml 中无法调用在页面的 .js 中定义的函数,但是,wxml 中可以调用 wxs 中定义的函数。因此,小程序中 wxs 的典型应用场景就是“过滤器

9.1.3 wxs 和 JavaScript 的关系

虽然 wxs 的语法类似于 JavaScript,但是 WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致

9.2 基本语法

9.2.1 内嵌 wxs 脚本

wxs 代码可以编写在 wxml 文件中的 <wxs> 标签内,就像 JavaScript 代码可以编写在 html 文件中的 <script> 标签内一样。

wxml 文件中的每个 <wxs></wxs> 标签,必须提供 module 属性,用来指定当前 wxs 的模块名称,方便在 wxml 中访问模块中的成员

<view>{{ m1.toUpper(username) }}</view>

<wxs module='m1'>
	// 将文本转为大写形式 zs -> ZS
    // 向外暴露一个名叫 toUpper 的方法
    module.export.toUpper = function(str) {
    	// 将结果返回出去
    	return str.toUpperCase()
    }
</wxs>

9.2.2 定义外联的 wxs 脚本

wxs 代码还可以编写在以 .wxs 为后缀名的文件内,就像 JavaScript 代码可以编写在以 .js 为后缀名的文件中一样。

// tools.wxs 文件
// 将文本转为小写形式 ZS -> zs
function toLower(str) {
    return str.toLowerCase()
}

// 将 toLower 共享出去,供外界使用
module.export = {
    toLower: toLower
}

9.2.3 使用外联的 wxs 脚本

在 wxml 中引入外联的 wxs 脚本时,必须为 <wxs> 标签添加 module 和 src 属性,其中:

  • module 用来指定模块的名称
  • src 用来指定要引入的脚本的路径,且必须是相对路径
<!-- 调用 tools 模块中的方法 -->
<view>{{tools.toLower(username)}}</view>
<!-- 引入外联的 tools.wxs 脚本,并命名为 tools -->
<wxs src="../../utils/tools.wxs" module="tools"></wxs>

9.3 wxs 的特点

  1. 与 JavaScript 不同

    为了降低 wxs 的学习成本,wxs 语言在设计时借鉴了大量 JavaScript 的语法,但是在本质上,wxs 和 JavaScript 是完全不同的两种语言!

  2. 不能作为组件的事件回调

    wxs 典型的应用场景就是“过滤器”,经常配合 Mustache 语法进行使用,例如:

    <view>{{tools.toLower(username)}}</view>
    

    但是,在 wxs 中定义的函数不能作为组件的事件回调函数。例如,下面的用法是错误的:

    <button bindtap="tools.toLower"></button>
    
  3. 隔离性

    隔离性指的是 wxs 的运行环境和其他 JavaScript 代码是隔离的。体现在如下两方面:

    • wxs 不能调用 js 中定义的函数
    • wxs 不能调用小程序提供的 API
  4. 性能好
    • IOS 设备上,小程序内的 wxs 会比 JavaScript 代码快 2~20 倍
    • Android 设备上,二者的运行效率无差异

十、自定义组件

10.1 组件的创建与使用

10.1.1 创建组件

  1. 在项目的根目录中,鼠标右键,创建 components -> test 文件夹
  2. 在新建的 components -> test 文件夹上,鼠标右键,点击“新建Component”
  3. 键入组件的名称之后会漕河,会自动生成组件对应的 4 个为恩建,后缀名分别为 .js.json.wxml.wxss

注意:为了保证目录结构的清晰,建议把不同的组件,存放到单独的目录中,例如:

10.1.2 引用组件

组件的引用方式分为“局部引用”和“全局引用”

  • 局部引用

    1. 组件只能在当前被引用的页面内使用。
    2. 在页面的 .json 配置文件中引用组件的方式,叫做“局部引用”
    // 在页面的 .json 文件中,引入组件
    {
      "usingComponents": {
    	  "my-test": "/components/test/test"
      }
    }
    
    // 在页面的 .wxml 文件中,使用组件
    <my-test></my-test>
    
  • 全局引用

    1. 组件可以在每个小程序页面中使用
    2. 在 app.json 全局配置文件中引用组件的方式,叫做“全局引用”
    // 在页面的 app.json 文件中,引入组件
    {
        "pages": []
        "window": {}
      	"usingComponents": {
    	  "my-test": "/components/test/test"
      	}
    }
    
    // 在页面的 .wxml 文件中,使用组件
    <my-test></my-test>
    

10.1.3 组件和页面

从表面来看,组件和页面都是由 .js.json.wxml.wxss 这四个文件组成的。但是,组件和页面的 .js.json 文件有明显的不同:

  • 组件的 .json 文件中需要声明 "component": true 属性

    {
      "component": true
    }
    
  • 组件的 .js 文件中调用的是 Component() 函数

    // components/test/test.js
    Component({
    	/**
    	 * 组件的属性列表
    	 */
    	properties: {},
    
    	/**
    	 * 组件的初始数据
    	 */
    	data: {},
    
    	/**
    	 * 组件的方法列表
    	 */
    	methods: {}
    })
    
  • 组件的事件处理函数需要定义到 methods 节点中

10.2 样式

10.2.1 组件样式隔离

默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的 UI 结构。

好处:

  • 防止外界的样式影响组件内部的样式
  • 防止组件的样式破坏外界的样式

10.2.2 组件样式隔离的注意点

  • app.wxss 中的全局样式对组件无效
  • 只有 class 选择器会有样式隔离效果,id 选择器、属性选择器、标签选择器不受样式隔离的影响

建议:在组件和引用组件的页面中建议使用 class 选择器,不要使用id 选择器、属性选择器、标签选择器!

10.2.3 修改组件的样式隔离选项

默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望在外界能够控制组件内部的样式,此时,可以通过 styleIsolation 修改组件的样式隔离选项,用法如下:

// 在组件的 .js 文件中新增如下配置
Component({
  options: {
    styleIsolation: 'isolated'
  }
})

// 或在组件的 .json 文件中新增如下配置
{
    styleIsolation: 'isolated'
}

styleIsolation 的可选值:

10.3 数据、方法和属性

10.3.1 data 数据

在小程序中,用于组件模板渲染的私有数据,需要定义到 data 节点中,示例如下:

Component({
    /**
	 * 组件的初始数据
	 */
    data: {
        count: 0
    }
})

10.3.2 methods 方法

在小程序中,事件处理函数自定义方法需要定义到 methods 节点中,示例如下:

Component({
    /**
	 * 组件的方法列表
	 */
	methods: {
        // 事件处理函数
		add() {
			this.setData({
				count: this.data.count + 1
			})
            this._showCount() // 通过 this 直接调用自定义方法
		},
        
        // 自定义方法建议以 '_' 开头,以区分事件处理函数和自定义方法
		_showCount() {
			wx.showToast({
				title: 'count的值是:' + this.data.count,
				icon: 'none'
			})
		}
	}
})

10.3.3 properties 属性

在小程序中,properties 是组件的对外属性,用来接收外界传递到组件中的数据,示例如下:

Component({
   /**
   * 组件的属性列表
   */
  properties: {
    // 第一种方式:简化定义属性的方式
    max: Number
    // 第二种方式:完整定义属性的方式【当需要指定属性默认值时,建议使用此方式】
    max: {
      type: Number, // 属性值的数据类型
      value: 10     // 属性默认值【如果外界没有传递 max 的值,默认等于 10】
    }
  },
})

<my-test1 max="10"></my-test1>

注意:上述代码中的 max 可以通过 this.properties.max 访问到。

10.3.4 data 和 properties 的区别

在小程序中,properties 属性和 data 数据的用法相同,它们都是可读可写的,只不过:

  • data 更倾向于存储组件的私有数据
  • properties 更倾向于存储外界传递到组件的数据
Component({
    methods:{
        showInfo() {
            console.log(this.data)       // 输出结果: {count: 0, max: 10}
            console.log(this.properties) // 输出结果: {count: 0, max: 10}
            // 结果为 true,证明 data 数据和properties 属性在本质上是一样的、都是可读可写的
            console.log(this.data === this.properties)
        }
    }
})

10.3.5 使用 setData 修改 properties 的值

由于 data 数据和 properties 属性在本质上没有任何区别,因此 properties 属性的值也可以用于页面渲染,或使用 setDataproperties 中的属性重新赋值,示例代码如下:

Component({
    properties: {
        // 定义属性
        max: Number
    }
    methods:{
    	addCount() {
    		// 使用 setData 修改属性的值
    		this.setData({
                max: this.properties.max + 1
            })
		}
    }
})

10.4 数据监听器

10.4.1 什么是数据监听器

数据监听器用于监听和响应任何属性和数据字段的变化,从而执行特定的操作。它的作用类似于 Vue 中的 watch 侦听器。在小程序组件中,数据监听器的基本语法格式如下:

Component({
    observers: {
        // 可以在一个监听器中监听多个字段,中间用逗号隔开。新值的顺序就是字段声明时的顺序
        '字段A, 字段B': function(字段A的新值, 字段B的新值) {
            // do something
        }
    }
})

10.4.2 监听对象属性的变化

Component({
    observers: {
        // 可以在一个监听器中监听多个字段,中间用逗号隔开。新值的顺序就是字段声明时的顺序
        '对象.属性A, 对象.属性B': function(属性A的新值, 属性B的新值) {
            // do something
        }
    }
})

触发上述代码中的监听器有三种情况:

  • 为属性 A 赋值

    使用 setData 设置 this.data.对象.属性A 时触发

  • 为属性 B 赋值

    使用 setData 设置 this.data.对象.属性B 时触发

  • 直接为对象赋值

    使用 setData 设置 this.data.对象 时触发

10.4.3 案列

需求:一个颜色可由 R、G、B 三个部分组成,在页面中创建一个 view ,其背景颜色动态绑定为属性 fullColor、创建三个按钮,每个按钮分别控制对象 rgb 中的属性 r、g、b,使用监听器监听这三个属性的变化,动态计算出 fullColor 的值,从而达到修改背景颜色的效果。

Component({
	/**
	 * 组件的初始数据
	 */
	data: {
		rgb: { // rgb 的颜色值对象
			r: 0,
			g: 0,
			b: 0
		},
		fullColor: '0, 0, 0' // 根据 rgb 对象的三个属性,动态计算 fullColor 的值
	}
})

步骤:

  • 在页面中定义元素并绑定数据

    <view class="box" style="background-color: rgb({{fullColor}});">rgb的值是:{{rgb.r}},{{rgb.g}},{{rgb.b}}</view>
    <view>
    	<text class="color" bindtap="addR">R</text>
    	<text class="color" bindtap="addG">G</text>
    	<text class="color" bindtap="addB">B</text>
    </view>
    
  • 在 wxss 中美化样式

    /* components/test2/test2.wxss */
    .box {
    	width: 100%;
    	height: 300rpx;
    	text-align: center;
    	line-height: 300rpx;
    	color: #fff;
    }
    
    .color {
    	display: inline-block;
    	width: 100rpx;
    	height: 100rpx;
    	text-align: center;
    	line-height: 100rpx;
    }
    
    .color:nth-child(1) {
    	background-color: red;
    }
    .color:nth-child(2) {
    	background-color: green;
    }
    .color:nth-child(3) {
    	background-color: blue;
    }
    
  • 定义 button 的事件处理函数

    methods: {
        addR() {
            // 让属性自增加 5,并且判断自增过后是否大于 255,如果是,则永远等于 255
            this.setData({ // 修改 rgb 对象上 r 属性的值
                'rgb.r': this.data.rgb.r + 5 > 255 ? 255 : this.data.rgb.r + 5
            })
        },
        addG() {
            this.setData({ // 修改 rgb 对象上 g 属性的值
                'rgb.g': this.data.rgb.g + 5 > 255 ? 255 : this.data.rgb.g + 5
            })
        },
        addB() {
            this.setData({ // 修改 rgb 对象上 b 属性的值
                'rgb.b': this.data.rgb.b + 5 > 255 ? 255 : this.data.rgb.b + 5
            })
        }
    }
    
  • 监听对象中指定属性的变化

    // 监听器
    observers: {
        // 函数中的参数为新值
        // 要监听的属性用单引号包裹
        // 监听多个属性用逗号隔开
        'rgb.r, rgb.g, rgb.b': function(r, g, b) {
            this.setData({
                // 为 data 中的 fullColor 重新赋值
                fullColor: `${r}, ${g}, ${b}`
            })
        }
    }
    

    如果某个对象中需要被监听的属性太多,为了方便,可以使用通配符 ** 来监听对象中所有属性的变化,示例代码如下:

    // 监听器
    observers: {
        // 监听对象中所有属性的变化,使用通配符 **
        // 只要任何一个属性发生变化都会触发监听器
        'rgb.**': function(obj) {
            this.setData({
                fullColor: `${obj.r}, ${obj.g}, ${obj.b}`
            })
        }
    }
    

10.5 组件的生命周期

10.5.1 组件全部的生命周期函数

小程序组件可用的全部生命周期如下表所示:

生命周期参数描述最低版本
created在组件实例刚刚被创建时执行1.6.3
attached在组件实例进入页面节点树时执行1.6.3
ready在组件在视图层布局完成后执行1.6.3
moved在组件实例被移动到节点树另一个位置时执行1.6.3
detached在组件实例被从页面节点树移除时执行1.6.3
errorObject Error每当组件方法抛出错误时执行2.4.1

其中,最重要的生命周期是 created attached detached ,包含一个组件实例生命流程的最主要时间点。

  • 组件实例刚刚被创建好时, created 生命周期被触发。此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data此时还不能调用 setData 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。
  • 在组件完全初始化完毕、进入页面节点树后, attached 生命周期被触发。此时, this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行(例如发请求获取初始数据)。
  • 在组件离开页面节点树后, detached 生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。

10.5.2 定义生命周期函数

生命周期方法可以直接定义在 Component 构造器的第一级参数中。

自小程序基础库版本 2.2.3 起,组件的的生命周期也可以在 lifetimes 字段内进行声明(这是推荐的方式,其优先级最高)。

代码示例

Component({
// 推荐用法
lifetimes: {
  attached: function() {
    // 在组件实例进入页面节点树时执行
  },
  detached: function() {
    // 在组件实例被从页面节点树移除时执行
  },
}
  
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached: function() {
  // 在组件实例进入页面节点树时执行
},
detached: function() {
  // 在组件实例被从页面节点树移除时执行
},
// ...
})

10.6 组件所在页面的生命周期

10.6.1 什么是组件坐在页面的生命周期

有时,自定义组件的行为依赖于页面状态的变化,此时就需要用到组件所在页面的生命周期,在 pageLifetimes 定义段中定义。其中可用的生命周期包括:

生命周期参数描述最低版本
show组件所在的页面被展示时执行2.2.3
hide组件所在的页面被隐藏时执行2.2.3
resizeObject Size组件所在的页面尺寸变化时执行2.4.0

代码示例

Component({
  pageLifetimes: {
    show: function() {
      // 页面被展示
    },
    hide: function() {
      // 页面被隐藏
    },
    resize: function(size) {
      // 页面尺寸变化
    }
  }
})

10.6.2 生成随机的 RGB 颜色值

在页面刚被展示的时候,生成随机的 RGB 颜色值

// pageLifetimes 节点可以访问组件所在页面的生命周期函数(只能访问三个)
pageLifetimes: {
    // 页面被展示
    show: function() {
        // 生成随机颜色
        this.setData({
            rgb: {
                r: Math.floor((Math.random()*256)),
                g: Math.floor((Math.random()*256)),
                b: Math.floor((Math.random()*256))
            }
        })
    }
}
  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值