【微信小程序开发学习篇】

微信小程序开发学习篇

概述

相关信息

文章目录

小程序基础

1、数据绑定与 Mustache(插值表达式) 语法

index.wxml:

<!--index.wxml-->
<view class="container">
  <!-- 双花括号包裹一个变量,这种语法称作“Musa -->
  {{info}}
</view>
<!-- 动态绑定属性值 -->
<image src="{{flower_path}}"></image>
<!-- Mustache 语法支持三元表达式 -->
<view>{{isMain ? '当前是首页' : '当前不是首页'}}</view>

index.js:

// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    'info':'hello world112',
    'flower_path':'/images/meinv.jpg',
    isMain: false
  },
})


2、事件绑定

什么是事件?
在这里插入图片描述
小程序中的常用事件
在这里插入图片描述
事件对象的属性列表
在这里插入图片描述
在这里插入图片描述

2.1 绑定 bindtap 事件
<!--index.wxml-->
<view>
<!-- 定义按钮组件,设置 tap(手指点击) 事件的处理函数为 btnBindHandler -->
<button type="warn" bindtap="btnBindHandler">按钮</button>
</view>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    'info':'hello world112',
    'flower_path':'/images/meinv.jpg',
    isMain: false
  },
  /**
   * 被绑定的事件处理函数
   * @param {*} event 事件对象
   */
  btnBindHandler(event){
    console.log('tap 事件被触发。。。');
    console.log(event);
  }
})

调试界面:
在这里插入图片描述

2.2 事件传参与数据同步

事件处理函数中为 data 对象中的数据赋值
在这里插入图片描述

示例:

// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    // data.count 原始值
    count: 0
  },
  /**
   * 被绑定的事件处理函数,与 data 对象同级
   * @param {*} event 事件对象
   */
  btnBindHandler(event) {
    // 修改 data.count 的值,通过 this.data.count 获取旧值,相当于 this 指向了传递给 Page 函数的整个对象参数
    console.log('原始值=',this.data.count);
    // 修改
    // 写法一:this.data.xxx = 新值
    // this.data.count++
    // 写法二:this.setData()
    this.setData({
      count: 100
    })
    console.log('新值=',this.data.count);
  }
})

2.3 事件传参

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
示例:

<!--index.wxml-->
<view>
  <!-- 如果此处 data-info 的属性值不使用双花括号包裹,则其数字 2 将被认为是字符串,而非数字 -->
  <button type="warn" bindtap="btnBindHandler" data-info="{{2}}">按钮</button>
</view>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    // data.count 原始值
    count: 0
  },
  /**
   * 被绑定的事件处理函数,与 data 对象同级
   * @param {*} event 事件对象
   */
  btnBindHandler(event) {
    console.log('按钮被点击...');
    // 事件传参含义:渲染层 wxml 的 button 组件被点击后,其属性 data-info="{{2}}" 被传递到逻辑层 .js 中的对应的处理函数 btnBindHandler 的 event.target.dataset 对象中,此过程称为“事件传参”
    // event.target.dataset 是一个对象,存储了事件传递的参数
    console.log(event.target.dataset);
    console.log(event.target.dataset.info);
  }
})
2.4 bindinput 事件

在这里插入图片描述
示例:

<!--index.wxml-->
<view>
  <!-- input 组件用于用户输入文本,bindinput 属性即绑定了 input 事件,属性值为对应的事件处理函数 -->
  <input bindinput="inputHandler" ></input>
</view>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  /**
   * 被绑定的事件处理函数,与 data 对象同级
   * @param {*} event 事件对象
   */
  inputHandler(event) {
    console.log('input 事件被调用。。。');
    // event.detail.value 可获取 input 组件输入后的新的文本内容
    console.log(event.detail.value);
  }
})
2.5 实现文本框和 data 之间的数据同步

在这里插入图片描述
示例:

<!--index.wxml-->
<view>
  <!-- input 组件用于用户输入文本,bindinput 属性即绑定了 input 事件,属性值为对应的事件处理函数 -->
  <!-- input 的 value 属性指定在页面中的显示值,这里直接与 js 的 data 进行绑定 -->
  <input value="{{msg}}" bindinput="inputHandler" ></input>
</view>
/**index.wxss**/
.userinfo {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #aaa;
}

input {
  border: 1px solid rgb(42, 110, 211);
  padding: 3px;
  border-radius: 4px;
}

.userinfo-avatar {
  overflow: hidden;
  width: 128rpx;
  height: 128rpx;
  margin: 20rpx;
  border-radius: 50%;
}

.usermotto {
  margin-top: 200px;
}
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    // 定义一个属性,使其被 input 组件进行解析渲染
    msg: 'hello,'
  },
  /**
   * 被绑定的事件处理函数,与 data 对象同级
   * @param {*} event 事件对象
   */
  inputHandler(event) {
    console.log('input 事件被调用。。。');
    // 获取输入框的值,并赋值给 data.msg
    this.setData({
      msg: event.detail.value
    })
  }
})

3、条件渲染(wx:if)

在这里插入图片描述

3.1 简单使用 wx:if

示例:

<!--index.wxml-->
<view>
  <!-- 条件渲染 -->
  <!-- 以下三个组件分别展示三个文本内容,但是它们使用 wx:if 语句来决定该组件是否被渲染,它们是一个逻辑的整体,相当于一个 if-elif-else 语句 -->
  <view wx:if="{{type === 1}}"></view>
  <view wx:elif="{{type === 2}}"></view>
  <view wx:else>保密</view>
</view>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    // 定义一个属性,使其决定 wxml 中应该渲染哪一个组件
    type: 3
  },
})

3.2 block 组件结合 wx:if

在这里插入图片描述
示例:

<!--index.wxml-->
<!-- 被渲染 -->
<block wx:if="{{true}}">
  <view>a</view>
  <view>b</view>
</block>
<!-- 不被渲染,且页面中不存在该组件,即该组件不被创建,而非被隐藏 -->
<block wx:if="{{false}}">
  <view>c</view>
  <view>d</view>
</block>
3.3 hidden 属性控制组件隐藏

示例:

<!--index.wxml-->
<!-- hidden 属性控制该组件是否被隐藏 ,true 则隐藏,false 则显示-->
<view hidden="{{flag}}">hello world</view>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    // 定义一个属性,使其决定 wxml 中是否隐藏一个组件
    flag: true
  },
})
3.4 wx:if 与 hidden 属性的区别

在这里插入图片描述

4、列表渲染

4.1 wx:for

在这里插入图片描述
示例:

<!--index.wxml-->
<!-- 列表渲染,使用 wx:for 属性 -->
<view wx:for="{{arr1}}">
索引是:{{index}}, 值是:{{item}}
</view>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    // 定义一个数组,在 wxml 中使用 wx:for 来进行遍历渲染
    arr1: ['apple','huawei','vivo']
  },
})

模拟器界面:
在这里插入图片描述

4.2 手动指定 元素索引名和当前项变量名

在这里插入图片描述

4.3 wx:key

在这里插入图片描述

5、wxss 设置组件样式

  • WXSS (WeiXin Style Sheets)是一套样式语言,用于美化WXML的组件样式,类似于网页开发中的CSS。
  • WXSS 与 CSS 的关系
    在这里插入图片描述
5.1 rpx 单位
  • rpx ( responsive pixel)是微信小程序独有的,用来解决屏适配的尺寸单位。

  • 实现原理
    在这里插入图片描述

  • rpx 与 px 单位的换算
    在这里插入图片描述
    因为使用 ipone6 设备进行单位换算时,两者总是两倍的关系,因此比较容易计算一些,建议使用它。

5.2 @import 进行样式导入
  • 使用WXSS提供的@import语法,可以导入外联的样式表。
  • 语法格式
    在这里插入图片描述
    示例:
/* common.wxss */
.username {
  color: red;
}
/**index.wxss**/
/* 导入公共样式文件 */
@import '/common/common.wxss';
.userinfo {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #aaa;
}

input {
  border: 1px solid rgb(42, 110, 211);
  padding: 3px;
  border-radius: 4px;
}

.userinfo-avatar {
  overflow: hidden;
  width: 128rpx;
  height: 128rpx;
  margin: 20rpx;
  border-radius: 50%;
}

.usermotto {
  margin-top: 200px;
}
<!--index.wxml-->
<view class="username">marry</view>
5.3 设置全局样式 与 局部样式
  • 定义在 app.wxss 中的样式为全局样式,作用于每一个页面。
  • 在页面的 .wxss 文件中定义的样式为局部样式,只作用于当前页面。

注意:

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

6、全局配置

6.1 常用的全局配置及小程序的窗口组成

在这里插入图片描述

6.2 window 对象的配置

在这里插入图片描述

在这里插入图片描述

6.3 设置导航栏标题
  • 设置步骤:app.json -> window -> navigationBarTitleText
6.4 设置导航栏背景色
  • 设置步骤:app.json -> window -> navigationBarBackgroundColor
6.5 设置导航栏标题文本颜色
  • 设置步骤:app.json -> window -> navigationBarTextStyle
  • 目前小程序的导航栏标题只支持 black、white 两种颜色。
6.6 全局开启下拉刷新页面
  • 概念:下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。
  • 设置步骤:app.json -> window -> 把 enablePullDownRefresh 的值设置为 true。
  • 注意:在 app.json 中启用下拉刷新功能,会作用于每个小程序页面!
  • app.json -> window -> backgroundColor 设置下拉刷新时窗口的背景色。
  • app.json -> window -> backgroundTextStyle 设置下拉刷新时的窗口的小圆点的颜色,只有 dark 、light 两种颜色。
6.7 设置上拉触底功能
  • 概念:上拉触底是移动端的专有名词,通过手指在屏幕上的上拉滑动操作,从而加载更多数据的行为。
  • 设置步骤:app.json -> window -> 为 onReachBottomDistance 设置新的数值(不需要加单位)。
  • 注意:默认距离为50px,如果没有特殊需求,建议使用默认值即可。
6.8 设置 tabBar

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 设置步骤:在 app.json 中,添加一个 tabBar 对象,该对象与 pages、window 同级。
    示例:
// app.json
{
  "pages": [
    "pages/index/index",
    "pages/list/list",
    "pages/logs/logs"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#00ff00",
    "navigationBarTitleText": "我的第一个微信小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true,
    "backgroundColor": "#4b3c5d"
  },
  // 需要添加的 tabBar 对象
  "tabBar": {
    // list 数组中的元素即为每一个 tab 项
    "list": [
      // tab 项中 pagePath 设置该 tab 被点击后跳转到的页面
      // text 设置该 tab 的文本内容
      {
        "pagePath": "pages/index/index",
        "text": "index"
      },
      {
        "pagePath": "pages/list/list",
        "text": "list"
      }
    ]
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

7、页面配置(局部配置)

  • 小程序中,每个页面都有自己的 .json 配置文件,用来对当前页面的窗口外观、页面效果等进行配置。
  • 页面配置和全局配置的关系:

小程序中,app.json中的 window节点,可以全局配置小程序中每个页面的窗口表现。

如果某些小程序页面想要拥有特殊的窗口表现,此时,“页面级别的 .json 配置文件”就可以实现这种需求。

注意:当页面配置与全局配置冲突时,根据就近原则,最终的效果以页面配置为准。
在这里插入图片描述

8、发起网络数据请求

8.1 GET 和 POST 请求
  • 小程序的网络请求的限制
    在这里插入图片描述

  • 配置 request 合法域名

  • 需求描述:假设在自己的微信小程序中,希望请求https://www.escook.cn/域名下的接口。
  • 配置步骤:登录微信小程序管理后台->开发-→开发设置→>服务器域名->修改request合法域名 。

在这里插入图片描述

  • 发起 get 请求
    示例:
    1)配置 request 合法域名
    2)在项目中编写代码
<!--index.wxml-->
<!-- 测试发起网络数据请求 -->
<button bindtap="sendGetReq">发起GET请求</button>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  /**
   * 设置按钮的事件处理函数,用于发起 GET 请求
   */
  sendGetReq() {
    console.log('按钮被点击 ~~~');
    // 调用 wx.request() 发起网络请求
    wx.request({ // 接收一个配置对象
      url: 'https://www.escook.cn/api/get',
      method: 'GET', // 设置请求方式
      data: { // 设置发送到服务器的数据
        name: '公孙离',
        age: 23
      },
      // 设置请求成功的回调函数
      success(res) {
        console.log('请求成功。');
        console.log(res);
      }
    })
  }
})
  • 发起 POST 请求
    示例:
<!--index.wxml-->
<!-- 测试发起网络数据请求 -->
<button bindtap="sendPostReq">发起POST请求</button>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  // 发起 POST 请求
  sendPostReq() {
    console.log('按钮被点击 ~~~');
    // 调用 wx.request() 发起网络请求
    wx.request({ // 接收一个配置对象
      url: 'https://www.escook.cn/api/post',
      method: 'POST', // 设置请求方式
      data: { // 设置发送到服务器的数据
        name: '李白',
        age: 23
      },
      // 设置请求成功的回调函数
      success(res) {
        console.log('POST请求成功。');
        console.log(res);
        console.log(res.data);
      }
    })
  }
})

8.2 在页面加载时请求数据

在这里插入图片描述

8.3 跳过 request 合法域名校验

在这里插入图片描述

8.4 跨域和 Ajax 问题
  • 跨域问题只存在于基于浏览器的Web开发中。由于小程序的宿主环境不是浏览器,而是微信客户端。所以小程序中不存在跨域的问题。
  • Ajax技术的核心是依赖于浏览器中的 XMLHttpRequest 这个对象,由于小程序的宿主环境是微信客户端,所以小程序中不能叫做“发起 Ajax 请求”,而是叫做“发起网络数据请求”。

总结 1

在这里插入图片描述

9、页面导航

  • 什么是页面导航

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

1)<a> 链接
2) location. href

  • 小程序实现页面导航的两种方式
    在这里插入图片描述
9.1 导航到 tabBar 页面

示例:

<!--index.wxml-->

<!-- navigator 组件进行声明式导航 -->
<!-- 
1、url 属性指定跳转到某个路径下的哪个页面,且属性值必须以 / 开头;
2、open-type 属性指定导航的方式,若为 switchTab 则表示跳转到一个 tab 页面,且如果需要跳转到 tabBar 页面,则必须声明:open-type="switchTab" -->
<navigator url="/pages/list/list" open-type="switchTab">跳转到 list 页面</navigator>
9.2 跳转到非 tabBar 页面

在这里插入图片描述

  • 注意:当需要导航到非 tabBar 页面时,甚至可以省略 open-type=“navigate” 。

示例:

<!--index.wxml-->
<!-- 测试发起网络数据请求 -->
<button bindtap="sendPostReq">发起POST请求</button>

<!-- navigator 组件进行声明式导航 -->
<!-- 
1、url 属性指定跳转到某个路径下的哪个页面,且属性值必须以 / 开头;
2、open-type 属性指定导航的方式,如果需要跳转到非 tabBar 页面,则必须声明:open-type="navigate" 
3、当需要导航到非 tabBar 页面时,甚至可以省略 open-type="navigate" -->
<navigator url="/pages/logs/logs" open-type="navigate">跳转到 logs 页面</navigator>
9.3 后退导航

在这里插入图片描述
示例:

<!-- 后退导航 -->
<!-- 必须指定 open-type="navigateBack" ,且 delta 属性指定具体返回几个页面 -->
<navigator open-type="navigateBack" delta="1">返回上一页面</navigator>
  • 注意:为了简便,如果只是后退到上一页面,则可以省略 delta 属性,因为其默认值就是 1。
9.5 编程式导航
  • 导航到 tabBar 页面
    在这里插入图片描述

示例:

<!--index.wxml-->
<view>首页</view>
<!-- 编程式导航 -->
<!-- 定义一个按钮,监听其点击事件,绑定事件处理函数,事件处理函数中进行编程式导航 -->
<button bindtap="toListPage">导航到 list 页面</button>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  // 事件处理函数,用于导航到 tabBar 页面 list
  toListPage(e) {
    // 编程式导航
    // wx.switchTab() 方法用于导航到 tabBar 页面
    wx.switchTab({
      // url 直接指定页面路径
      url: '/pages/list/list',
    })
  }
})
  • 跳转到非 tabBar 页面
    在这里插入图片描述

  • 后退导航
    在这里插入图片描述

9.6 导航传参
  • 声明式导航传参
    在这里插入图片描述

示例:

<!--index.wxml-->
<view>首页</view>
<!-- 导航传参 -->
<!-- 
  1、直接在 url 的路径中跟上 ?参数键值对即可,多个键值对用 & 隔开
  2、此方式只能用于跳转到非 tabBar 页面
   -->
<navigator url="/pages/logs/logs?name=李白&age=23" >跳转到 logs</navigator>

效果:
在这里插入图片描述

  • 编程式导航传参
    在这里插入图片描述

  • 在 onLoad 函数中接收导航参数
    在这里插入图片描述
    示例:

// logs.js
Page({
  data: {
    // 一般使用 query 属性接收页面导航过来传递的参数
    query: {}
  },
  onLoad(options) {
    console.log(options);
    this.setData({
      query: options
      })
    }
  })

10、下拉刷新事件

  • 下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。
  • 开启下拉刷新方法
    在这里插入图片描述
  • 在全局或页面的 .json 配置文件中,通过 backgroundColor 和 backgroundTextStyle 来配置下拉刷新窗口的样式,其中:

1)backgroundColor 用来配置下拉刷新窗口的背景颜色,仅支持16进制的颜色值。
2)backgroundTextStyle 用来配置下拉刷新loading 的样式,仅支持dark和 light。

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

参考代码:

// logs.js
const util = require('../../utils/util.js')

Page({
  data: {
  },
  /**
   * 当用户进行下拉刷新时被调用
   */
  onPullDownRefresh() {
    console.log('用户进行下拉刷新动作 ~~');
    // 真机运行时,下拉刷新动作执行后不会自动关闭该 loading 效果,此时我们可以手动执行 wx.stopPullDownRefresh() 即可关闭下拉刷新效果
    wx.stopPullDownRefresh({
      success: (res) => {console.log('下拉动作完成。');},
    })
  }
})

11、上拉触底事件

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

  • 在页面的 .js 文件中,通过 onReachBottom() 函数即可监听当前页面的上拉触底事件。示例代码如下:

  • 配置上拉触底距离:
    上拉触底距离指的是触发上拉触底事件时,滚动条距离页面底部的距离。
    可以在全局或页面的 .json 配置文件中,通过 onReachBottomDistance 属性来配置上拉触底的距离。小程序默认的触底距离是 50px,在实际开发中,可以根据自己的需求修改这个默认值。

示例:

// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  // 监听上拉触底事件的处理函数,当发生上拉触底事件时被调用
  onReachBottom() {
    console.log('触发了上拉触底事件 ~~');
  }
})

12、自定义编译模式

  • 适用情景:比如我们需要编译后直接显示指定的页面,或者设置进入页面的请求参数等等。
  • 设置方法:
    在这里插入图片描述

13、小程序的生命周期

13.1 概念

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.2 应用生命周期函数

示例:

// app.js
App({

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

  },

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

  },

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

  },

  /**
   * 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
   */
  onError: function (msg) {
    // 自定义异常输出消息
    console.log('程序运行时发生错误: ', msg);
  }
})


13.3 页面的生命周期函数

示例:

// pages/message/message.js
Page({

  /**
   * 页面的初始数据
   */
  data: {

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
	// 常用于页面加载时,请求初始化数据
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
	// 常用于页面加载完成后,修改初始化的数据
  },

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

  },

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

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  }
})

14、WXS 脚本

14.1 概念
  • WXS ( WeiXin Script)是小程序独有的一套脚本语言,结合 WXML,可以构建出页面的结构。
  • wxml 中无法调用在页面的 .js 中定义的函数,但是,wml 中可以调用 wxs 中定义的函数。因此,小程序中 WXS 的典型应用场景就是“过滤器”。
  • wxs 与 js 的关系:
    在这里插入图片描述
14.2 内嵌 wxs 脚本

在这里插入图片描述

示例:

<!--index.wxml-->
<view>首页</view>
<!-- wxml 文件中使用 wxs 脚本 -->
<!-- 
  1、调用 wxs 脚本导出的函数,需要使用 Mustache 语法,如:模块名.函数名
  2、传递的参数可以是页面 .js 文件的 data 对象属性值,也可以是自定义的基本类型数据,如字符串
 -->
<view>{{m1.toUpper(username)}}</view>
<view>{{m1.toUpper('gongsunli')}}</view>

<!-- 定义 wxs 模块 -->
<wxs module="m1">
  // 导出一个 toUpper 函数,该函数功能为:将字符串转为大写字母
  module.exports.toUpper = function (str) {
    return str.toUpperCase()
  }
</wxs>
// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    username: 'libai'
  },
})

效果:
在这里插入图片描述

14.3 使用外联的 wxs 脚本

在这里插入图片描述
在这里插入图片描述

示例:

/** /utils/wxs/tools.wxs */
// 定义需要导出的函数,用于将文本转为小写
function toLower(str) {
  return str.toLowerCase()
}

// 导出函数
module.exports = {
  toLower: toLower
}

<!--index.wxml-->
<view>首页</view>
<!-- wxml 文件中使用 wxs 脚本 -->
<!-- 
  1、调用 wxs 脚本导出的函数,需要使用 Mustache 语法,如:模块名.函数名
  2、传递的参数可以是页面 .js 文件的 data 对象属性值,也可以是自定义的基本类型数据,如字符串
 -->
<view>{{m1.toUpper(username)}}</view>
<view>{{m1.toUpper('gongsunli')}}</view>
<view>{{m2.toLower('HELLO,WORLD.')}}</view>

<!-- 定义 wxs 模块 -->
<wxs module="m1">
  // 导出一个 toUpper 函数,该函数功能为:将字符串转为大写字母
  module.exports.toUpper = function (str) {
    return str.toUpperCase()
  }
</wxs>

<!-- 导入一个外联的 wxs 脚本 -->
<!-- 需要声明 src 属性,属性值为外联 wxs 脚本文件的相对路径,同时指定 module 属性 -->
<wxs src='../../utils/wxs/tools.wxs' module="m2"></wxs>
14.4 wxs 的特点
  • 为了降低 wxs (Weixin Script)的学习成本, wxs 语言在设计时借大量鉴了JavaScript 的语法。但是本质上,wxs 和 JavaScript 是完全不同的两种语言!

  • 不能作为组件的事件回调
    在这里插入图片描述

  • 隔离性好
    在这里插入图片描述

  • 性能较好
    在这里插入图片描述

总结 2

在这里插入图片描述

15、基础加强

在这里插入图片描述

在这里插入图片描述

15,1 自定义组件

1)创建组件
在这里插入图片描述

2)引用组件
在这里插入图片描述

  • 局部引用组件
    在这里插入图片描述
    示例:
    index.json
{
  "usingComponents": {
    "my-test01": "/components/test/test"
  }
}
<!--index.wxml-->
<view>首页</view>
<!-- 使用页面 .json 导入的自定义组件 -->
<my-test01></my-test01>
<!-- components/test1/test.wxml -->
<text>components/test1/test.wxml</text>

效果:
在这里插入图片描述

  • 全局引用组件
    在这里插入图片描述
    示例:
    app.json
{
  "pages": [
    "pages/index/index",
    "pages/list/list",
    "pages/logs/logs"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#00ff00",
    "navigationBarTitleText": "我的第一个微信小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true,
    "backgroundColor": "#4b3c5d"
  },
  "tabBar": {
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "index"
      },
      {
        "pagePath": "pages/list/list",
        "text": "list"
      }
    ]
  },
  "usingComponents": {
    "my-test01": "/components/test/test"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}
  • 全局引用 VS 局部引用
    在这里插入图片描述

在这里插入图片描述

15.2 自定义组件的样式
  • 隔离性
    在这里插入图片描述
    在这里插入图片描述
  • 修改组件的隔离性
    在这里插入图片描述
    在这里插入图片描述
15.3 自定义组件的 data、methods、properties
  • 定义自定义组件的 data
    在这里插入图片描述

  • 定义自定义组件的 methods 方法

  • 定义自定义组件的属性
    在这里插入图片描述
    示例:

<!-- components/test1/test.wxml -->
<text class="red-color-text">components/test1/test.wxml</text>
<view>
  <text>{{count}}</text>
  <button bindtap="addCount">+1</button>
</view>
// components/test1/test.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    // 第一种方式:简化写法,不指定默认值
    max: Number,
    // 第二种方式:属性值是一个配置对象,可以指定默认值
    min: {
      type: Number,
      value: 10
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    // 一个计数器变量
    count: 0
  },

  /**
   * 组件的方法列表
   */
  methods: {

    // 让 this.data.count 加 1 的事件处理函数
    addCount() {
      if (this.properties.max <= this.data.count) return
      this.setData({
        count: this.data.count + 1
      })
    },
    // 自定义方法
    _getMax() {
      console.log('当前组件设置的最大值=', this.properties.max);
    }
  }
})
  • data 和 properties 的区别
    在这里插入图片描述
    实际上 data 和 properties 指向的是同一个对象,只不过为了区别它们的作用,使用了两个不同的变量名来表示而已。

在这里插入图片描述

15.4 数据监听器

在这里插入图片描述

  • 监听 data 中的数据变化
    即在自定义组件的 .js 文件中,创建于 data、methods 同一级别的 observers 对象,其中定义监听变量名的方法即可。
    示例:
<!--components/test2/test2.wxml-->
<text>components/test2/test2.wxml</text>
<!-- 定义 view ,显示 data 中三个变量的值 -->
<view>{{n1}} + {{n2}} = {{sum}}</view>
<!-- 另一种写法 -->
<!-- <view>{{n1}} + {{n2}} = {{n1 + n2}}</view> -->
<!-- 定义按钮,让变量 n1 +1 -->
<button bindtap="addN1">n1+1</button>
<view></view>
<button bindtap="addN2">n2+1</button>
// components/test2/test2.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
    // 定义三个变量,初始值为 0
    n1: 0,
    n2: 0,
    sum: 0
  },

  /**
   * 组件的方法列表
   */
  methods: {
    // 事件处理函数,分别处理 n1+1、n2+1
    addN1() {
      this.setData({
        n1: this.data.n1 + 1
      })
    },
    addN2() {
      this.setData({
        n2: this.data.n2 + 1
      })
    }
  },
  /**
   * 定义数据监听器
   */
  observers: {
    // 监听 data.n1、data.n2 的数据变化
    // 只要监听的变量中有一个发生变化,该函数就会被自动调用
    // 属性名就是要监听的变量名,多个变量名之间以逗号分隔
    // 方法参数对应着监听的变量名,是对应变量名的新值
    'n1,n2': function (newN1, newN2) {
      // 修改 data.sum 的值
      this.setData({
        sum: newN1 + newN2
      })
    }
  }
})
  • 监听对象属性值的变化
    在这里插入图片描述
    在这里插入图片描述
15.5 纯数据字段
  • 概念
    在这里插入图片描述

  • 使用规则
    在这里插入图片描述

15.6 组件的全部生命周期函数
  • 概念
    在这里插入图片描述

  • 主要的组件生命周期函数及特点
    在这里插入图片描述

  • 方式一:与 data 同级,创建组件的生命周期函数即可,此方式是旧版本的定义方式,此时已经不再推荐使用

// components/test2/test2.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
  },
  /**
   * 定义生命周期函数方式一:与 data 同级,创建生命周期函数即可,此方式是旧版本的定义方式,此时已经不再推荐使用
   */
  created(){
    console.log('created');
  },
  attached(){
    console.log('attached');
  },
  ready(){
    console.log('ready');
  },
  moved(){
    console.log('moved');
  },
  detached(){
    console.log('detached');
  },
  error(err){
    console.log('err');
  }
})
  • 方式二:与 data 同级,创建 lifetimes 对象,在对象中定义组件的生命周期函数即可,此方式是新版本的定义方式,此时推荐使用
// components/test2/test2.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
  },
  /**
   * 定义生命周期函数方式二:与 data 同级,创建 lifetimes 对象,在对象中定义生命周期函数即可,此方式是新版本的定义方式,此时推荐使用
   */
  lifetimes: {
    created() {
      console.log('created ~~');
    },
    attached() {
      console.log('attached ~~');
    },
    ready() {
      console.log('ready ~~');
    },
    moved() {
      console.log('moved ~~');
    },
    detached() {
      console.log('detached ~~');
    },
    error(err) {
      console.log('err ~~');
    }
  }

})
15.7 组件所在页面的生命周期函数

在这里插入图片描述
在这里插入图片描述
示例:

// components/test2/test2.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
  },
  /**
   * 定义组件生命周期函数方式二:与 data 同级,创建 lifetimes 对象,在对象中定义生命周期函数即可,此方式是新版本的定义方式,此时推荐使用
   */
  lifetimes: {
    created() {
      console.log('created ~~');
    },
    attached() {
      console.log('attached ~~');
    },
    ready() {
      console.log('ready ~~');
    },
    moved() {
      console.log('moved ~~');
    },
    detached() {
      console.log('detached ~~');
    },
    error(err) {
      console.log('err ~~');
    }
  },
  /**
   * 定义组件在页面中时触发的生命周期函数
   * 定义方法:将 show、hide、resize 方法定义在 pageLifetimes 对象中即可
   */
  pageLifetimes: {
    show() {
      console.log('show');
    },
    hide() {
      console.log('hide');
    },
    resize() {
      console.log('resize');
    }
  }

})
15.8 自定义组件的插槽
  • 概念
    在这里插入图片描述

  • 单个插槽用法
    在这里插入图片描述

示例:
自定义组件中定义插槽

<!--components/test2/test2.wxml-->
<text>components/test2/test2.wxml</text>
<view></view>
<!-- 定义一个插槽 slot 组件,用于将此插槽的空间交给使用此组件的使用者进行填充 -->
<slot></slot>

页面中使用组件时,填充插槽内容

<!--index.wxml-->
<view class="red-color-text">首页</view>
<!-- my-test02 组件中定义了一个 slot 插槽,我们可以把填充内容写在 my-test02 标签中 -->
<my-test02>hello world</my-test02>
  • 启用多个插槽
    默认情况下,微信小程序不支持多个插槽的用法。如果非要使用,需要进行配置。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    示例:
    定义多个插槽的只定义组件的 .js 文件中定义 options.multipleSlots
    =true,从而开启多个插槽的使用
// components/test2/test2.js
Component({
  /**
   * 对组件进行配置的对象
   */
  options: {
    // 配置组件可以使用多个插槽
    multipleSlots: true
  },
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
  },
  /**
   * 定义组件生命周期函数方式二:与 data 同级,创建 lifetimes 对象,在对象中定义生命周期函数即可,此方式是新版本的定义方式,此时推荐使用
   */
  lifetimes: {
    created() {
      console.log('created ~~');
    },
    attached() {
      console.log('attached ~~');
    },
    ready() {
      console.log('ready ~~');
    },
    moved() {
      console.log('moved ~~');
    },
    detached() {
      console.log('detached ~~');
    },
    error(err) {
      console.log('err ~~');
    }
  },
  /**
   * 定义组件在页面中时触发的生命周期函数
   * 定义方法:将 show、hide、resize 方法定义在 pageLifetimes 对象中即可
   */
  pageLifetimes: {
    show() {
      console.log('show');
    },
    hide() {
      console.log('hide');
    },
    resize() {
      console.log('resize');
    }
  }

})

然后在自定义组件中定义多个插槽

<!--components/test2/test2.wxml-->
<text>components/test2/test2.wxml</text>
<view></view>
<!-- 定义多个插槽,每个插槽需要使用 name 属性进行命名 -->
<slot name='before'></slot>
<view>我是插槽内部的组件 D</view>
<slot name='center'></slot>
<slot name='after'></slot>

最后在使用自定义组件的页面中声明指定插槽填充的组件即可

<!--index.wxml-->
<view class="red-color-text">首页</view>
<!-- my-test02 组件中定义了多个 slot 插槽,我们可以把填充内容写在 my-test02 标签中 -->
<my-test02>
  <!-- 为了使用定义好 name 名称的插槽,我们需要传递一个组件,组件使用 slot 属性指定需要填充到哪个插槽的插槽名称 -->
  <view slot='center'>center</view>
  <view slot='after'>after</view>
  <view slot='before'>before</view>
</my-test02>
15.9 父子组件的通信方式
  • 父子组件之间的三种通信方式
    在这里插入图片描述

  • 父传子:属性绑定
    在这里插入图片描述
    在这里插入图片描述

示例:
子组件的 .js 定义相关的 properties 属性

// components/test2/test2.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    // 为了接收父组件传递的数据,我们需要在 properties 中定义相关的属性名,并声明其数据类型
    // 父组件将传输一个字符串给自己,自己用 properties.username 进行接收
    username: String 
  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
  },
  
})

父组件在使用子组件时进行属性绑定(其实就是定义相关的属性名和属性值)

<!--index.wxml-->
<view class="red-color-text">首页</view>
<view>父组件中 username={{username}}</view>
<view>---------------------</view>
<!-- my-test02 组件即为自定义组件,同时也是当前页面的子组件,我们可以使用属性绑定的方法向子组件传值(只能传输基本数据类型的值) -->
<!-- 进行属性绑定时,我们使用 Mustache 语法赋值给子组件的对应属性名 -->
<my-test02 username="{{username}}"></my-test02>

子组件获取到父组件传递的数据后,进行 UI 渲染

<!--components/test2/test2.wxml-->
<text>components/test2/test2.wxml</text>
<view></view>
<!-- 渲染父组件传递过来的值,此时该值已经被赋予给自己的 properties 对象的属性了,因此可以直接进行访问 -->
<view>父组件传递而来的 username={{username}}</view>

效果:
在这里插入图片描述

  • 子传父:事件绑定
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
示例:
父组件 .js

// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
    count: 1
  },
  // 父组件定义自定义事件处理函数
  syncCount(event) {
    console.log('syncCount');
    // 事件对象会保存子组件调用 this.triggerEvent() 时传递的第二个参数对象在 e.detail 中
    console.log(event);
    // 获取到了子组件传递数据,我们就可以动态更新父组件的数据了
    this.setData({
      count: event.detail.value
    })
  }
})

父组件 .wxml

<!--index.wxml-->
<view class="red-color-text">首页</view>
<view>父组件中 count={{count}}</view>
<view>---------------------</view>
<!-- 父组件使用子组件时,使用 bind:sync (冒号可省略,冒号后面的是事件名称,可以任意定义)绑定父组件定义好的事件处理函数 -->
<my-test02 count="{{count}}" bind:sync="syncCount"></my-test02>

子组件 .js

// components/test2/test2.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    // 为了接收父组件传递的数据,我们需要在 properties 中定义相关的属性名,并声明其数据类型
    // 父组件将传输一个数值给自己,自己用 properties.count 进行接收
    count: Number
  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
    // 让 properties.count+1 的方法
    addCount() {
      this.setData({
        count: this.properties.count + 1
      })
      // 子组件中主动触发父组件定义的自定义事件(然后父组件自动调用相关的处理函数),从而将数据同步给父组件
      // 调用 this.triggerEvent() 第一个参数传递父组件声明的自定义事件名称,即 bind: 后面的内容。第二个参数可以传递一个配置对象,在父组件被调用的处理函数中,会被保存在事件对象中,即 e.detail,所以这个配置对象我们可以直接将子组件的 properties 的属性值传递过去
      this.triggerEvent('sync', { value: this.properties.count })
    }
  },

})

子组件 .wxml

<!--components/test2/test2.wxml-->
<text>components/test2/test2.wxml</text>
<view></view>
<!-- 渲染父组件传递过来的值,此时该值已经被赋予给自己的 properties 对象的属性了,因此可以直接进行访问 -->
<view>子组件中:父组件传递而来的 count={{count}}</view>
<!-- 使用按钮控制自己的 count +1 -->
<button bindtap="addCount">count+1</button>

效果:当点击一次子组件的 count+1 按钮时,控制台 syncCount(event) 输出的 event 对象
在这里插入图片描述

  • 父访问子:获取子组件实例,从而直接获得或修改子组件的任意数据和方法,即 父组件调用 this.selectComponent()
    在这里插入图片描述

示例:
子组件 .js

// components/test2/test2.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    count: 0
  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
    
  },

})

父组件 .wxml

<!--index.wxml-->
<view class="red-color-text">首页</view>
<view>---------------------</view>
<!-- 父组件获取子组件实例 -->
<!-- 使用子组件时,需要指定 id 或 class 属性,便于选中此子组件 -->
<my-test02 class="child"></my-test02>
<!-- 点击按钮,触发获取子组件实例的处理函数 -->
<button bindtap="getChild">获取子组件</button>

父组件 .js

// index.js
// 获取应用实例
const app = getApp()

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  // 获取子组件实例的事件处理函数
  getChild() {
    // 调用 this.selectComponent() 获取子组件实例
    const child = this.selectComponent('.child')
    console.log(child);
    // 根据获取到的子组件实例对象 child ,我们可以调用该对象的方法或修改该对象的数据
    child.setData({
      count: 100
    })
  }
})
15.10 自定义组件的 behaviors
  • 概念
    在这里插入图片描述

  • behaviors 的工作方式
    在这里插入图片描述

  • 创建 behavior
    在这里插入图片描述

  • 导入并使用 behavior
    在这里插入图片描述
    实际上不止是自定义组件 Component,就来页面 Page 也可以导入 behavior ,从而获得共享的数据和方法。
    示例:
    在 .js 文件中创建并导出一个 Behavior 对象

/** behaviors/my-behavior.js */
// 导出一个 Behavior 实例
// Behavior 实例定义需要共享的数据和方法
module.exports = Behavior({
  data: {
    username: '公孙离'
  },
  properties: {

  },
  methods: {

  }
})

自定义组件或页面的 .js 文件中导入对应的 Behavior 对象,并设置 options.behaviors 数组

// index.js
// 获取应用实例
const app = getApp()
// 使用 require 函数获取 Behavior 实例
const behavior = require('../../behaviors/my-behavior')

Page({
  // 声明 behaviors 数组,其中存储导入的 Behavior 实例
  // 然后该数组中所有的 behavior 实例所具有的数据和方法将被共享给自身
  behaviors: [behavior],
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  
})

在页面或自定义组件中使用 behavior 共享的数据或方法

<!--index.wxml-->
<view class="red-color-text">首页</view>
<!-- 使用 behavior 中定义的数据 -->
<view>在 behavior 中,用户名={{username}}</view>

效果:
在这里插入图片描述

  • Behavior 所有可用的节点
    在这里插入图片描述

  • 同名字段和方法重名的覆盖和组合规则
    在这里插入图片描述

官方文档解释:
在这里插入图片描述

总结 3

在这里插入图片描述

16、小程序使用 npm

  • 使用 npm 的限制
    在这里插入图片描述
16.1 安装 Vant Weapp
  • 什么是 Vant Weapp
    Vant weapp 是有赞前端团队开源的一套小程序 UI 组件库,助力开发者快速搭建小程序应用。它所使用的是 MIT 开源许可协议,对商业使用比较友好。
    官方文档地址

  • npm 安装 Vant Weapp
    在这里插入图片描述

详细步骤:参考官方文档
1)进入微信小程序项目中,观察是否含有 package.json 包管理文件,如果没有该文件,可以执行

npm init -y

2)执行:通过 npm 工具安装该依赖,并指定安装版本为 1.3.3

npm i @vant/weapp@1.3.3 -S --production

3)使用开发者工具构建 npm 包
在这里插入图片描述

4)修改 app.json
将 app.json 中的 "style": "v2" 去除,小程序的新版基础组件强行加上了许多样式,难以覆盖,不关闭将造成部分组件样式混乱。

5)配置 project.config.json

{
  ...
  "setting": {
    ...
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram/"
      }
    ]
  }
}

6)使用 Vant Weapp 组件
在这里插入图片描述
示例:
app.json 的 usingComponents 对象中导入 Vant Weapp 组件

{
  "pages": [
    "pages/index/index",
    "pages/list/list",
    "pages/logs/logs"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#00ff00",
    "navigationBarTitleText": "我的第一个微信小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true,
    "backgroundColor": "#4b3c5d"
  },
  "tabBar": {
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "index"
      },
      {
        "pagePath": "pages/list/list",
        "text": "list"
      }
    ]
  },
  "usingComponents": {
    "van-button": "@vant/weapp/button/index"
  },
  "sitemapLocation": "sitemap.json"
}

在 .wxml 中使用组件

<!--index.wxml-->

<!-- 使用 Vant Weapp 框架提供的 button 按钮,样式由 type 属性决定 -->
<van-button type='info'>按钮</van-button>
  • 使用 CSS 变量控制 Vant Weapp 组件样式

定制全局主题样式
Vant Weapp使用CSS变量来实现定制主题。关于CSS变量的基本用法,请参考MDN文档:

MDN 文档

在这里插入图片描述
所有可用的 CSS 变量参考:官方描述官方 CSS 变量配置文件

示例:

/**app.wxss**/
/* 配置 Vant Weapp 组件主体样式的颜色变量 */
page {
  --button-info-background-color: yellow;
}

17、小程序 API 的 Promise 化

  • 概念
    在这里插入图片描述

什么是 APl Promise 化?
API Promise 化,指的是通过额外的配置,将官方提供的、基于回调函数的异步 API,升级改造为基于 Promise 的异步 APl,从而提高代码的可读性、维护性,避免回调地狱的问题。

  • 实现小程序 API 的 Promise 化
    在这里插入图片描述

  • 安装 npm 包 miniprogram-api-promise

执行

npm i --save miniprogram-api-promise@1.0.4

然后重新使用微信开发者工具构建 npm 包(如果发现构建后出现异常情况,可以先将项目中的 miniprogram_npm 目录先删除干净,再重新构建 npm 包)

然后
在这里插入图片描述

  • 在 app.js 定义 Promise 化代码
// app.js
// 使用 es6 的 import 语法导入 miniprogram-api-promise 的 promisifyAll 函数
import {promisifyAll} from 'miniprogram-api-promise'

// console.log(promisifyAll);
// console.log(wx.p);
// 首先在全局的 wx 对象上绑定一个 p 对象,该对象初始化为 {},然后使用 wxp 变量指向 wx.p 对象
const wxp = wx.p = {}
/**
 *  在小程序入口文件中(app.js),只需调用一次 promisifyAl1 ()方法,即可实现异步 API 的 Promise 化
 */
promisifyAll(wx,wxp)

App({
  // some codes...
})
  • 调用 Promise 化之后的异步 API
    在这里插入图片描述

示例:
wxml 中定义按钮,该按钮绑定处理函数

<!--index.wxml-->
<view class="red-color-text">首页</view>
<!-- 使用 Vant Weapp 框架提供的 button 按钮,样式由 type 属性决定 -->
<!-- 绑定点击事件的处理函数为 getInfo -->
<van-button type='info' bindtap="getInfo">getInfo 按钮</van-button>

声明一个异步函数,其中调用已经 Promise 化的对象方法

// index.js
// 获取应用实例
const app = getApp()


Page({
  data: {
  },
  // 获取网络数据的方法
  // 我们可以在方法名前使用 async 关键字,声明该函数为一个异步函数
  async getInfo() {
    // 发起网络数据请求
    // 本来需要调用 wx.request() 方法来进行请求,但该方法是一个异步 API ,将来代码可维护性、可读性差
    // 我们调用已经 Promise 化的 API 函数来完成
    // 在 app.js 中,我们已经将所有 Promise 化后的函数绑定到了 wx.p 对象上,因此,我们直接调用它
    // 最终,它的返回值就是一个 Promise 对象
    // 我们可以在执行该方法代码前使用 await 关键字,然后该方法就可以直接返回 promise[[PromiseResult]] 结果对象了
    const res = await wx.p.request({
      url: 'https://www.escook.cn/api/get',
      method: 'get',
      data: {
        name: '王昭君',
        age: 26
      }
    })
    console.log(res);
    // 最终的 res.data 就是我们所需要的结果对象
    console.log('res.data=', res.data);
  }
})

调试结果:
在这里插入图片描述

18、全局数据共享方案和 MobX

  • 概念
    在这里插入图片描述
    在这里插入图片描述

  • 安装 MobX 的相关包
    在这里插入图片描述

终端执行

npm i --save mobx-miniprogram@4.13.2 mobx-miniprogram-bindings@1.2.1  

然后构建 npm 即可。

  • 创建 Store 实例
    在这里插入图片描述
    示例:
/** store/store.js */
// 使用这个 JS 文件,专门用来创建 Store 实例对象
// 导入 mobx-miniprogram 模块的 observerable 方法,用于获取 Store 实例
// 导入 action 函数,用于生成修改 store 实例数据的修改方法
import { observable, action } from 'mobx-miniprogram'

// 根据传入的配置对象创建 Store 实例,并导出
export const store = observable({
  // 定义数据字段
  numA: 100,
  numB: 33,
  // 定义计算属性
  // 使用 get 关键字进行表示该属性值可读不可修改
  // 方法名 sum 就是计算属性的访问名称
  get sum() {
    // 返回值就是计算属性的值
    return this.numA + this.numB
  },
  // 定义 actions 方法,专门用于修改 store 实例的数据
  updateNumA: action(function (step) {
    // console.log(this.numA);
    // 让 numA 直接加上传入的参数值
    this.numA += step
  }),
  updateNumB: action(function (step) {
    this.numB += step
  })
})
  • 绑定 Store 实例对象数据到页面 .Page 对象中
    在这里插入图片描述
    示例:
// index.js
// 获取应用实例
const app = getApp()

// 导入 mobx-miniprogram-bindings 模块中的 createStoreBindings 函数,用于绑定 Store 对象数据到 Page 对象中
import { createStoreBindings } from 'mobx-miniprogram-bindings'
// 导入自定义的 Store 对象实例,我们将从该对象中获取共享的数据
import { store } from '../../store/store'

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  /**
   * 将获取 Store 实例数据的代码写到 onLoad 生命周期函数中
   */
  onLoad(options) {
    // createStoreBindings 函数返回一个绑定对象,接收这个对象有助于我们使用它在 onUnLoad 函数中进行清理和关闭工作
    // 调用此方法时,我们第一个参数传入当前的 Page 对象作为上下文对象,然后第二个参数是一个包含 Store 对象数据的配置对象,将 store 的数据进行绑定到 Page.data 上
    this.storeBindings = createStoreBindings(this, {
      // store 属性定义数据源,即需要获取的 Store 实例
      store,
      // fields 数组指定需要从数据源中获取哪些数据对象
      // 数组中直接指定 store 中的数据字段属性名的字符串或计算属性的方法名字符串
      fields: ['numA', 'numB', 'sum'],
      // actions 数组指定需要从数据源获取哪些可以修改 Store 实例数据的修改方法
      // 数组中直接指定该修改方法的方法名字符串
      actions: ['updataNumA']
    })
    console.log(this.data);
  },
  onUnload() {
    // 进行绑定对象的关闭和清理工作
    this.storeBindings.destroyStoreBindings()
  }
})

  • 在页面 Page 中使用 Store 实例的数据
    在这里插入图片描述

示例:

<!--index.wxml-->
<view class="red-color-text">首页</view>
<view>{{numA}} + {{numB}} = {{sum}}</view>
<!-- 使用一个导入的 button 组件,调用处理函数时,传递 step=1 参数给事件对象 -->
<van-button type='primary' bindtap="btnHandler1" data-step="{{1}}">numA +1</van-button>
<van-button type='warning' bindtap="btnHandler1" data-step="{{-1}}">numA -1</van-button>
// index.js
// 获取应用实例
const app = getApp()

// 导入 mobx-miniprogram-bindings 模块中的 createStoreBindings 函数,用于绑定 Store 对象数据到 Page 对象中
import { createStoreBindings } from 'mobx-miniprogram-bindings'
// 导入自定义的 Store 对象实例,我们将从该对象中获取共享的数据
import { store } from '../../store/store'

Page({
  // data 对象设置对应的变量值,然后供 wxml 使用 Mustache 语法去引用
  data: {
  },
  /**
   * 将获取 Store 实例数据的代码写到 onLoad 生命周期函数中
   */
  onLoad(options) {
    // createStoreBindings 函数返回一个绑定对象,接收这个对象有助于我们使用它在 onUnLoad 函数中进行清理和关闭工作
    // 调用此方法时,我们第一个参数传入当前的 Page 对象作为上下文对象,然后第二个参数是一个包含 Store 对象数据的配置对象,将 store 的数据进行绑定到 Page.data 上
    this.storeBindings = createStoreBindings(this, {
      // store 属性定义数据源,即需要获取的 Store 实例
      store,
      // fields 数组指定需要从数据源中获取哪些数据对象
      // 数组中直接指定 store 中的数据字段属性名的字符串或计算属性的方法名字符串
      fields: ['numA', 'numB', 'sum'],
      // actions 数组指定需要从数据源获取哪些可以修改 Store 实例数据的修改方法
      // 数组中直接指定该修改方法的方法名字符串
      actions: ['updateNumA']
    })
    console.log(this.data);
  },
  onUnload() {
    // 进行绑定对象的关闭和清理工作
    this.storeBindings.destroyStoreBindings()
  },
  btnHandler1(e) {
    console.log('click.');
    console.log(e);
    // 使用 e.target.dataset.step 获取点击组件传递过来的参数值
    // 使用从 store 对象获取的修改 numA 数据字段的修改方法来修改 store 对象的 numA 数据
    // 实际上,当前 Page.data 获取到的是 store 数据的引用,而不是数据对象的复制,因此当 store 中的数据发生变化,当前 Page.data 中的数据也随着变化
    this.updateNumA(e.target.dataset.step)
  }
})
  • 在组件中使用 Store 数据
    在这里插入图片描述

示例:

// components/test1/test.js

// 按需导入 mobx-miniprogram-bindings.storeBindingsBehavior 对象和自定义的 Store 对象
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from '../../store/store'

Component({
  /**
   * 将 storeBindingsBehavior 存储到 behaviors 数组节点中
   */
  behaviors: [storeBindingsBehavior],
  /**
   * 声明 storeBindings 对象,将组件对象与 Store 对象进行绑定
   */
  storeBindings: {
    // 数据源 store
    store,
    // fields 对象,用于映射组件与 store 的属性
    fields: {
      // key 是组件中使用的 data 的属性名,value 是 store 中属性名字符串
      numA: 'numA',
      numb: 'numB',
      // 映射 store 的计算属性
      sum: 'sum'
    },
    // actions 对象,用于映射组件与 store 的修改自身数据方法
    actions: {
      // 映射规则如 fields 中的规则
      updateNumA: 'updateNumA'
    }
  },
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {

  }
})
  • 在组件中使用 Store 共享的数据
    在这里插入图片描述

示例:

<!-- components/test1/test.wxml -->
<text class="red-color-text">components/test1/test.wxml</text>
<view>{{numA}} + {{numb}} = {{sum}}</view>
<!-- 使用一个导入的 button 组件,调用处理函数时,传递 step=1 参数给事件对象 -->
<van-button type='primary' bindtap="btnHandler2" data-step="{{1}}">numB +1</van-button>
<van-button type='warning' bindtap="btnHandler2" data-step="{{-1}}">numB -1</van-button>
// components/test1/test.js

// 按需导入 mobx-miniprogram-bindings.storeBindingsBehavior 对象和自定义的 Store 对象
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from '../../store/store'

Component({
  /**
   * 将 storeBindingsBehavior 存储到 behaviors 数组节点中
   */
  behaviors: [storeBindingsBehavior],
  /**
   * 声明 storeBindings 对象,将组件对象与 Store 对象进行绑定
   */
  storeBindings: {
    // 数据源 store
    store,
    // fields 对象,用于映射组件与 store 的属性
    fields: {
      // key 是组件中使用的 data 的属性名,value 是 store 中属性名字符串
      numA: 'numA',
      numb: 'numB',
      // 映射 store 的计算属性
      sum: 'sum'
    },
    // actions 对象,用于映射组件与 store 的修改自身数据方法
    actions: {
      // 映射规则如 fields 中的规则
      updateNumB: 'updateNumB'
    }
  },
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
    btnHandler2(e) {
      // 获取组件点击时传递的事件对象中的数据
      this.updateNumB(e.target.dataset.step)
    }
  }
})

19、分包

19.1 概念

什么是分包?
分包指的是把一个完整的小程序项目,按照需求划分为不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。

分包的好处
对小程序进行分包的好处主要有以下两点:
1)可以优化小程序首次启动的下载时间
2)在多团队共同开发时可以更好的解耦协作。

分包前的项目构成
在这里插入图片描述

分包后的项目构成
在这里插入图片描述

分包的加载规则
在这里插入图片描述

分包的体积限制
在这里插入图片描述

19.2 配置分包

在这里插入图片描述

示例:
app.json 中 与 pages 节点同级创建 subpackages 节点对象,用于定义分包信息。

{
  // 主包页面列表 
  "pages": [
    "pages/index/index",
    "pages/list/list",
    "pages/logs/logs"
  ],
  // 分包列表,每个对象元素就是一个分包配置对象
  "subpackages": [
  	// 分包配置对象1
    {
      "root": "pkgA", // 分包名称(即目录名)
      "name": "p1", // 可以为当前分包设置别名
      // 当前分包所包含的页面列表
      "pages": [
      	// 一个分包页面的路径
        "pages/cat/cat",
        "pages/dog/dog"
      ]
    },
    {
      "root": "pkgB",
      "pages": [
        "pages/bird/bird"
      ]
    }
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#00ff00",
    "navigationBarTitleText": "我的第一个微信小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true,
    "backgroundColor": "#4b3c5d"
  },
  "tabBar": {
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "index"
      },
      {
        "pagePath": "pages/list/list",
        "text": "list"
      }
    ]
  },
  "usingComponents": {
    "my-test01": "/components/test/test",
    "my-test02": "/components/test2/test2",
    "van-button": "@vant/weapp/button/index"
  },
  "sitemapLocation": "sitemap.json"
}
  • 使用微信开发者工具查看当前分包体积信息
    点击【详情】–》【基本信息】–》【本地代码】进行查看分包信息
    在这里插入图片描述

  • 分包原则
    在这里插入图片描述

  • 分包之间的引用原则
    在这里插入图片描述

19.3 独立分包

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实际上,独立分包的配置方法就是在普通分包配置对象的基础上新增一个 =true 的属性即可。

示例:

{
  "pages": [
    "pages/index/index",
    "pages/list/list",
    "pages/logs/logs"
  ],
  "subpackages": [
    {
      "root": "pkgA",
      "name": "p1",
      "pages": [
        "pages/cat/cat",
        "pages/dog/dog"
      ]
    },
    {
      "root": "pkgB",
      "pages": [
        "pages/bird/bird"
      ],
      // 设置 independent 属性为 true,表示当前分包是一个独立分包
      "independent": true
    }
  ],
 // some codes...
}
  • 独立分包的引用原则
    在这里插入图片描述
19.4 分包预下载

什么是分包预下载
分包预下载指的是:在进入小程序的某个页面时,由框架自动预下载可能需要的分包,从而提升进入后续分包页面时的启动速度。

  • 配置分包预下载
    在这里插入图片描述

示例:

{
  "pages": [
    "pages/index/index",
    "pages/list/list",
    "pages/logs/logs"
  ],
  "subpackages": [
    {
      "root": "pkgA",
      "name": "p1",
      "pages": [
        "pages/cat/cat",
        "pages/dog/dog"
      ]
    },
    {
      "root": "pkgB",
      "pages": [
        "pages/bird/bird"
      ],
      "independent": true
    }
  ],
  // preloadRule 对象节点指定分包预下载功能
  "preloadRule": {
  	// 对象属性名就是跳转到哪个页面路径触发预下载功能
  	// 属性值表示跳转到这个页面后,会对对象中的 packages 数组声明的所有的分包(可以指定分包的 root 名或 name 别名)进行预下载; 而 network 属性指定了允许预下载功能在什么网络状态下可以执行,有 all(所有网络状态均可)、wifi(只允许 wifi 网络)两个有效值。
    "pages/list/list": {
      "packages": [
        "pkgA"
      ],
      "network": "all"
    }
  },
}

效果:控制台的 console 界面
在这里插入图片描述

  • 分包预下载的限制
    在这里插入图片描述

20、案例-自定义 tabBar

示例:
1)app.json 中的 tabBar 节点对象声明 custom=true

{
  "pages": [
    "pages/index/index",
    "pages/list/list",
    "pages/logs/logs"
  ],
  "subpackages": [
    {
      "root": "pkgA",
      "name": "p1",
      "pages": [
        "pages/cat/cat",
        "pages/dog/dog"
      ]
    },
    {
      "root": "pkgB",
      "pages": [
        "pages/bird/bird"
      ],
      "independent": true
    }
  ],
  "preloadRule": {
    "pages/list/list": {
      "packages": [
        "pkgA"
      ],
      "network": "all"
    }
  },
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#00ff00",
    "navigationBarTitleText": "我的第一个微信小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true,
    "backgroundColor": "#4b3c5d"
  },
  "tabBar": {
    "custom": true,
    // 这个 list 数组不能少,原因:为了保证低版本兼容以及区分哪些页面是 tab 页
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "index"
      },
      {
        "pagePath": "pages/list/list",
        "text": "list"
      }
    ]
  },
  "usingComponents": {
    "my-test01": "/components/test/test",
    "my-test02": "/components/test2/test2",
    "van-button": "@vant/weapp/button/index"
  },
  "sitemapLocation": "sitemap.json"
}

2)项目中新建 custom-tab-bar 目录,然后在其中创建 index Component。
在这里插入图片描述
模拟器界面中底部位置出现红框的信息即为成功。

3)我们使用 Vant Weapp 框架提供的 tabBar 组件
app.json 导入相关组件

{
	// some codes...
	"usingComponents": {
	    "van-tabbar": "@vant/weapp/tabbar/index",
	    "van-tabbar-item": "@vant/weapp/tabbar-item/index"
  	},
  // some codes...
}

4)在 tabBar 指定的 custom-tab-bar/index.wxml 中使用组件

<!--custom-tab-bar/index.wxml-->
<!-- 当 tabbar 发生改变之后,会触发 change 事件,从而调用 onChange 函数;active 属性表示当前选中的 tab 索引,从 0 开始编号 -->
<van-tabbar active="{{ activeTabarIndex }}" bind:change="onChange">
  <!-- info 属性表示当前 tab 右上角的徽标值,若为 '' 或 0,则不进行渲染,若为其他数字,则会进行渲染  -->
  <van-tabbar-item wx:for="{{list}}" wx:key="index" info="{{item.info && item.info > 0 ? item.info : ''}}">
    <!-- slot='icon' 表示当前图标或图片是当前 tab 不被选中时启用,而 slot='icon-active' 则表示当前图标或图片是当前 tab 被选中时启用 -->
    <image slot="icon" src="{{ item.icon.normalPath }}" mode="aspectFit" style="width: 30px; height: 18px;" />
    <image slot="icon-active" src="{{ item.icon.activePath }}" mode="aspectFit" style="width: 30px; height: 18px;" />
    <!-- tab 的文本内容直接在 van-tabbar-item 中声明即可 -->
    {{item.text}}
  </van-tabbar-item>
</van-tabbar>

5)修改 store 对象的数据,将徽标数与被选中的 tab 索引交给它来控制

/** store/store.js */
// 使用这个 JS 文件,专门用来创建 Store 实例对象
// 导入 mobx-miniprogram 模块的 observerable 方法,用于获取 Store 实例
// 导入 action 函数,用于生成修改 store 实例数据的修改方法
import { observable, action } from 'mobx-miniprogram'

// 根据传入的配置对象创建 Store 实例,并导出
export const store = observable({
  // 定义数据字段
  numA: 10,
  numB: 0,
  // 表示当前选中的 tab 索引值
  activeTabbarIndex: 0,
  // 定义计算属性
  // 使用 get 关键字进行表示该属性值可读不可修改
  // 方法名 sum 就是计算属性的访问名称
  // sum 就表示了徽标数
  get sum() {
    // 返回值就是计算属性的值
    return this.numA + this.numB
  },
  // 定义 actions 方法,专门用于修改 store 实例的数据
  updateNumA: action(function (step) {
    console.log('store.numA=', this.numA);
    // 让 numA 直接加上传入的参数值
    this.numA += step
  }),
  updateNumB: action(function (step) {
    console.log('store.numB=', this.numB);
    this.numB += step
  }),
  updateActiveTabbarIndex: action(function (index) {
    this.activeTabbarIndex = index
  })
})

6)在 custom-tab-bar/index.js 中编写代码逻辑

// custom-tab-bar/index.js

// 按需导入 mobx-miniprogram-bindings.storeBindingsBehavior 对象和自定义的 Store 对象
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from '../store/store'

Component({
  /**
 * 将 storeBindingsBehavior 存储到 behaviors 数组节点中
 */
  behaviors: [storeBindingsBehavior],
  /**
   * 声明 storeBindings 对象,将组件对象与 Store 对象进行绑定
   */
  storeBindings: {
    // 数据源 store
    store,
    // fields 对象,用于映射组件与 store 的属性
    fields: {
      // key 是组件中使用的 data 的属性名,value 是 store 中属性名字符串
      // activeTabarIndex 表示当前被选中的 tab 索引
      activeTabarIndex: 'activeTabarIndex',
      // 映射 store 的计算属性
      // sum 就是当前的徽标数
      sum: 'sum'
    },
    // actions 对象,用于映射组件与 store 的修改自身数据方法
    actions: {
      // 映射规则如 fields 中的规则
      updateActiveTabbarIndex: 'updateActiveTabbarIndex'
    }
  },
  /**
   * 数据监听器
   */
  observers: {
    'sum': function (val) {
      console.log(val);
      // 为第二个 tab 的 info 属性赋值
      this.setData({
        'list[1].info': val
      })
    }
  },
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {

    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        icon: {
          normalPath: "../images/home.png",
          activePath: "../images/home-active.png"
        }
      },
      {
        "pagePath": "pages/list/list",
        "text": "消息",
        icon: {
          normalPath: "../images/cart.png",
          activePath: "../images/cart-active.png"
        },
        // 徽标数据,新消息数量
        info: 0
      },
      {
        "pagePath": "pages/logs/logs",
        "text": "联系我们",
        icon: {
          normalPath: "../images/my.png",
          activePath: "../images/my-active.png"
        }
      }
    ]
  },

  /**
   * 组件的方法列表
   */
  methods: {
    onChange(event) {
      // event.detail 的值为当前选中项的索引
      // this.setData({ active: event.detail });
      // 修改 store 中的公共数据:当前被选中的 tab 索引
      this.updateActiveTabbarIndex(event.detail)
      console.log(event.detail);
      // 当前选中项的索引发生变化后,立即跳转到对应页面去
      wx.switchTab({
        // 被选中的 tab 索引与 list 中 tab 的索引一致
        url: '/' + this.data.list[event.detail].pagePath,
      })
    },
  }
})

效果:
在这里插入图片描述

总结 4

在这里插入图片描述

个人观点

至此,本文档编写告一段落,感谢各位网友的点评与观看。如有错误,请指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值