小程序组件进阶,又是强大的一天

1,044 阅读13分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情

重点

  • 小程序的数据响应
  • 小程序的事件注册
  • 小程序的生命周期
  • 小程序网络请求和常用API

数据定义和绑定

通过前面学习,掌握了小程序页面的布局,然而小程序页面中显示的内容都是静态的,真正线上运行的小程序内容都应该是动态的,接下来我们学习如何在小程序中处理页面数据响应

目标

页面数据定义和绑定

  • 小程序内容数据是由 JavaScript 控制提供的。每个页面都是由 .wxml、.wxss、.js、.json 构成,其中 wxml 定义结构,wxss 定义表现、json 进行配置, .js 则专门处理逻辑。
  • 所谓数据绑定是指数据与页面中组件的关联关系。使用 Mustache 语法(双大括号)将数据变量包起来。和vue用法类似。

定义数据

使用Page()方法,声明data数据对象

示例

page1.js

// Page 是全局提供的,用来创建一个页面实例
Page({
    // 通过 data 属性,初始化页面中用到的数据
    data: {
      // 字符串类型
      message: 'hello world!',
    }
});

使用数据

在wxml中,使用 Mustache 语法(双大括号)将数据变量包起来

<text class="msg">{{message}}</text>

效果

如下图:

数据定义和绑定-复杂数据

Page({
  // 通过 data 属性,初始化页面中用到的数据
  data: {
    user: {
      name: '小明',
      age: 16
    },
    courses: ['wxml', 'wxss', 'javascript']
  }
});
<text>我叫{{user.name}},我今年{{user.age}}岁了,我在学习{{courses[0]}}课程。</text>

绑定属性的值

目标

对元素上的属性进行动态绑定

语法

属性名="{{data}}"

示例

  data: {
    addClass: true,
    url:'/pages/meng/index',
  }   
<view class="{{addClass?'className1':'className2'}}">
    <text>动态class</text>
  <navigator url="{{url}}">go</navigator>
</view>

插值表达式

{{ }} 可以写表达式

Page({
  // 通过 data 属性,初始化页面中用到的数据
  data: {
    a: 10,
    b: 5,
    flag: true
  }
});
<text>{{a}} + {{b}} = {{a + b}}</text>
<text>{{flag ? '是': '否'}}</text>

简单列表渲染

目标

使用 wx:for渲染列表数据

通过 wx:for 控制(类似vue中的指令v-for)生成多个元素。

语法

<组件 wx:for="{{arr}}"></组件>

  1. wx:for 属性将当前组件按着数组的长度动态创建
  2. 通过内置默认的 index 变量可以访问到数组的索引值
  3. 通过内置默认的item变量可以访问到单元值

示例

Page({
  // 通过 data 属性,初始化页面中用到的数据
  data: {
    users: [
      {id:1, name: '小明', age: 16, gender: '男'},
      {id:2, name: '小刚', age: 19, gender: '男'},
      {id:3, name: '小红', age: 18, gender: '女'},
      {id:4, name: '小丽', age: 17, gender: '女'}
    ]
  }
});
<view wx:for="{{users}}">
  <text>{{index+1}}</text>
  <text>{{item.name}}</text>
  <text>{{item.age}}</text>
  <text>{{item.gender}}</text>
</view>

注意

  1. 要记得写{{}}
  2. 花括号和引号之间如果有空格,将最终被解析成为字符串。

<view wx:for="users"> 这里忘记了写{{}}

<view wx:for="{{users}} "> 这里有空格

<view wx:for="{{users}}"> 这里没有空格

列表渲染的key值

文档

当使用 wx:for 遍历数据时,会有如下图所示的警告,其原因是为了提升性能,建议添加 wx:key 属性。

wx:key 的值以两种形式提供:

  1. 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
<view wx:for="{{users}}" wx:key="id">
  <text>{{index+1}}</text>
  <text>{{item.name}}</text>
  <text>{{item.age}}</text>
  <text>{{item.gender}}</text>
</view>

列表渲染-支持对Object类型的遍历渲染(了解)

支持对Object类型的遍历渲染

.js

Page:{
  data: {
    obj: {
      a: 1,
      b: 2,
    }
 }  
} 

.wxml

<text wx:for="{{obj}}">{{index}}, {{item}}</text>

复杂列表渲染

目标

指定索引值变量、单元值变量

wx:for 嵌套

指定索引值变量、单元值变量

<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
  {{idx}}: {{itemName.message}}
</view>

通过 wx:for 对数组数据进行遍历时,可以分别指定访问数组索引值变量和单元值的变量。

  • wx:for-index 指定索引值变量,wx:for-item 指定单元值变量。

示例

Page({
  // 通过 data 属性,初始化页面中用到的数据
  data: {
    brands: [
      {
        name: '耐克',
        origin: '美国',
        category: ['男装', '女装', '鞋', '体育用品']
      },
      {
        name: 'SK-II',
        origin: '韩国',
        category: ['防晒霜','面膜', '洗护']
      }
    ]
  }
});
<view wx:for="{{brands}}" wx:for-index="k" wx:for-item="v">
  <view>
    <text>{{k+1}} </text>
    <text> {{v.name}} </text>
    <text> {{v.origin}}</text>
  </view>
  <view>
    <text wx:for="{{v.category}}">{{item}}</text>
  </view>
</view>

执行结果

条件渲染

本节目标: 根据条件控制是否渲染某个(些)元素

语法:wx:if="{{布尔值}}"

  1. 基本用法
<view wx:if="{{true}}">
  <text>看不到我?</text>
</view>
  1. 多分支
<view wx:for="{{users}}">
  <text>{{index+1}} </text>
  <text> {{item.name}}</text>
  <text> {{item.age}} </text>
  <text wx:if="{{item.age <= 14}}"> 儿童</text>
  <text wx:elif="{{item.age < 18}}"> 未成年</text>
  <text wx:else> 成年人</text>
</view>
Page({
  // 通过 data 属性,初始化页面中用到的数据
  data: {
    users: [
      {name: '小明', age: 16},
      {name: '小红', age: 13},
      {name: '小丽', age: 19}
    ]
  }
});

wx:if

自学:developers.weixin.qq.com/miniprogram…

事件处理-事件监听

小程序中的事件同网页 DOM 中的事件含义类似,只是语法及及其执行细节上略有差异。

注册事件

小程中通过属性为组件添加事件的监听。

格式

<组件 bind:事件名称="回调函数"></组件>

或者

<组件 bind事件名称="回调函数"></组件> (bind之后没有:)

示例:按钮上的点击tap事件

结构: page1.wxml

<!-- 为button组件绑定了 tap 事件,当用户点击了,会执行 sayHi 函数 -->
<button type="primary" bind:tap="sayHi">点我试试</button>

代码:page1.js

Page({
  // 事件回调函数
  sayHi: function () {
    console.log('Hi~')
  }
});

说明:tap 事件,相当于 html 中的 click事件

常用的事件

tap, input, change, focus, blur

  1. 监听表单的 blur、 focus事件:
Page({
  // 事件回调函数
  sayHi: function () {
    console.log('Hi~');
  },
  sayBye: function () {
    console.log('Bye~');
  }
});
<view> 
  <label for="">姓名: </label>
  <input type="text" bind:focus="sayHi" bind:blur="sayBye" />
</view>
<view> 
  <label for="">密码: </label>
  <input type="text"/>
</view>

小结

以前在学习 DOM 时见到的事件,绝大多数在小程序中都是被支持的

扩展阅读:事件文档

事件处理-事件对象

当某个事件被触发时,通过一个对象可以获得被触发事件的相关信息。

事件对象,回调函数的第一个参数即为事件对象。

事件对象基本使用

结构

<view bind:tap="sayHi">点我试试</view>

代码

Page({
  // 事件回调函数
  sayHi: function (ev) {
    // ev 即为事件对象,包含了事件相关信息
    console.log(ev);
  }
});

下面是事件对象的常用属性:

type: string 
timeStamp
target: 触发该事件的源头组件,而currentTarget则是当前事件所绑定的组件
currentTarget
detail:表单元素的值
touches 触摸事件,当前触摸点信息
changedTouches 触摸事件,当前变化的触摸点信息的数组

事件对象传参

注意:

  • 小程序中不支持事件回调函数在模版中传递参数
  • 获取元素data-params自定义属性值=>推荐使用event对象的target去获取对应触发元素自定义属性的值

说明:event.target 获取触发事件的元素属性,例如自定义属性

Page({
  // 事件回调函数
  sayHi: function (ev) {
    // ev 即为事件对象,包含了事件相关信息
    // 着重关注 ev.target.dataset 它可以获得 wxml 组件中以 data- 开头的自定义属性
    console.log(ev.target.dataset);
  }
});
<view class="child" data-name="小明" data-age="16" bind:tap="sayHi">点我试试</view>

事件冒泡

小程序事件处理机制与DOM类似,冒泡现象依然存在,但并非所有事件都会冒泡。[查看-事件分类-列表说明]((developers.weixin.qq.com/miniprogram…)

<view class="parent" bind:tap="foo">
  <view class="child" bind:tap="bar"></view>
</view>
Page({
  // 事件回调函数
  foo: function () {
    console.log('parent');
  },
  bar: function () {
    console.log('child');
  }
});

如下图所示,当点击 .child 盒子时,同时触发了 .child 和 .parent 的 tap事件

如果想要阻止冒泡现象的发生,可以使用小程序提供了另一种事件监听的方式,即 catch:事件名称="回调函数" bind 和 catch 的区别就在于是否阻止冒泡。

<view class="parent" bind:tap="foo">
  <view class="child" catch:tap="bar"></view>
</view>

如下图所示,当点击子盒子,父盒子的事件不会被触发了

通过事件冒泡来理解target和currentTarget

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

Page({
  // 事件回调函数
  sayHi: function (ev) {
    // ev 即为事件对象,包含了事件相关信息
    // 着重关注 ev.target.dataset 它可以获得 wxml 组件中以 data- 开头的自定义属性
    console.log(ev.target.dataset);
  },
  
  sayHello: function (ev) {
    console.log(ev.target.dataset);
  }
});
<view class="parent" data-name="大明" data-age="40" bind:tap="sayHello">
    <view class="child" data-name="小明" data-age="16" bind:tap="sayHi">点我试试</view>
</view>

数据更新

目标

修改了状态之后,视图对应更新

例如:

Page({
​
  /**
   * 页面的初始数据
   */
  data: {
    name: '小花',
    age: 20
  },
​
  onChangeMsg: function(e) {
    console.log(e.target.dataset.new)
    // 
  }

视图

<text>{{name}}</text><button bind:tap="onChangeMsg" data-new="小明">改名字</button>

数据驱动视图

核心API: Page.prototype.setData(data:object, callback:function)

字段类型必填描述最低版本
dataObject要改变的数据
callbackFunctionsetData引起的界面更新渲染完毕后的回调函数1.5.0

注意:

  • 第一个参数传入data变量中变量名和新值:{变量名 : 新值 }
  • 其中 变量名 可以以数据路径的形式给出,支持改变数组中的某一项或对象的某个属性,如 array[2].messagea.b.c.d

格式

this.setData({data中的属性名: 新值}, ()=>{ // setData引起的界面更新渲染完毕后的回调函数 })

示例代码

wxml:

<view>
  <text>{{msg}}</text>
  <button bindtap="rev">reverse</button>
    <!-- 列表 -->
  <view style="display:flex;flex-direction:column">
    <text wx:for="{{list}}">{{index+' '+item.name}}</text>
  </view>
  <button bindtap="changeArr">修改数组</button>
</view>  

js:

Page({
  data:{
    msg: 'hello wx!',
    list: [{ id: 1, name: 'html' }, { id: 2, name: 'css' }, { id: 3, name: 'js' }, { id: 4, name: 'java' }],
  },
  // 数据响应
  rev(){
    this.setData({
      // 反转文字
      msg: this.data.msg.split('').reverse().join('')
    })
  },
  // 修改数组
  changeArr() {
    this.setData({
      'list[3].name': 'python'
    },()=>{
      console.log(this.data.list)
    })
  }
})

注意

  1. 直接修改 this.data 而不调用 this.setData 是无法触发视图刷新,还会造成数据不一致

扩展阅读:Page方法详解 小程序宿主环境

生命周期-应用级别

目标:小程序的生命周期钩子函数

小程序是按着某种特定的流程序执行的,并且允许开发人员对执行过程中的若干节点进行事件监听,通常将这些节点称为生命周期,如小程序启动、销毁等。

复习:vue: 常用几个生命周期?

分类

小程序中将生命周期分成两类:

  1. 应用级别: 在 App()中触发
  2. 页面级别: Page()中触发

App入口

在小程序 app.js 文件中调用 App() 函数,可以注册一个小程序,应用级别的生命周期在这里进行监听。

// App 函数是小程序内置提供的,接受一个对象类型的数据做为参数。
// 注意大小写
App({
  
  // 小程序启动时
  onLaunch: function () {
    // 全局只会触发1次
    console.log('小程序启动了...');
  },
  // 小程序前台运行时
  onShow: function () {
    // 重复执行
    console.log('小程序前台运行了...');
  },
  // 小程序后台运行时
  onHide: function () {
    // 重复执行
    console.log('小程序后台运行了...');
  },
  // 执行错误时
  onError: function (err) {
    console.log('出错啦!!!');
  },
  // 冷启动(如扫码)打开小程序的页面不存在时
  onPageNotFound: function () {
    console.log('你找到页面去火星了???');
  }
})

注意:

  • 点击小程序右上角的胶囊的关闭时,并不会将小程序销毁而是处于后台运行的状态,当再次打开小程序时并不会触发 onLaunch 生命周期的监听,而是触发 onShow 将小程序重新前台运行
  • 关于前台和后台的理解可以简单的理解成最小化了,桌面上暂时看不到,但程序依然在运行,常见的如按了 home 键、接听电话等都会将小程序置于后台运行。
  • 是不是意味着小程序永远都不会被销毁?答案当然不是!小程序销毁是由微信自动控制的,一般情况下如果某个小程序后台运行超过一定时间或者系统内存不足报警时,微信会主动将小程序销毁,当再次从小程序列表找到这个小程序打开时,会再次触发 onLaunch 这个生命周期。

扩展阅读:运行机制

生命周期-Page页面

在小程序页面对应的 .js 文件中调用 Page() 函数,可以注册一个小程序页面,页面级别的生命周期在这里进行监听。

Page({
  // 页面加载时 vue=>created
  onLoad: function () {
    // 只会触发1次
    console.log('当前页面被加载了...');
  },
  
  // 页面显示时(返回、tabBar切换、前台运行)
  onShow: function () {
    // 重复触发
    console.log('当前页面显示了...');
  },
  // vue=> mounted
  onReady: function () {
    // 只会触发1次
    console.log('当前页面初次渲染完毕了...');
  },
  
  // 页面隐藏时(跳转、tabBar切换、后台运行)
  onHide: function () {
    console.log('当前页面不见了...');
  }
})

注意:

onShow 和 onHide 分别表示当前页面是否被显示/隐藏在当前小程序窗口,这其中包括几种情形:

  1. 页面跳转 => onHide
  2. 返回 => onShow
  3. tabBar => onSow 或 onHide
  4. 前台 => onShow
  5. 后台 => onHide

场景值-onLaunch

目标

掌握小程序场景值

场景值

打开小程序的方式多种多样,如扫码、转发、搜索、公众号等,通过场景值可以区分用户是以何种方式打开的小程序,通常用于条件判断或者数据统计等。

获得场景值

在小程序生命周期=>App()函数 onLaunch 和 onShow 中可以获得场景值:

App({
  onLaunch: function (res) {
    // 根据打开小程序的方式不同,res.scene 的值也不一样
    console.log(res.scene);
  },
  onShow: function (res) {
    // 这里也可以获取场景值
    console.log(res.scene);
  }
})

页面传参-onLoad

目标

query传参

为小程序组件 navigator 指定 url 属性,可以实现页面间的跳转,在跳转的同时支持通过 ? 向跳转页面传递参数。

格式

<navigator url="/pages/demo/index?参数1=值1&参数2=值2">跳转页面</navigator>

示例

<navigator url="/pages/demo/index?name=小明&age=18">跳转页面</navigator>

在被跳转的页面中通过监听 onLoad 生命周期获得地址中的参数

// pages/demo/index.js
Page({
  onLoad: function (query) {
    // 获取地址中的参数
    console.log(query);
  }
})

注意⚠️:如果跳转标签页页面,无法使用query形式传递参数

网络请求

目标

小程序发送请求获取后台数据

网络文档

基本使用

语法: wx.request(config:object)

示例代码:

测试接口:5990367be1e4470011c46fa8.mockapi.io/meng/user

测试接口:www.fastmock.site/mock/37d3b9…

Page({
    /**
   * 生命周期函数:vue=>created
   */
  onLoad: function () {
    // promise , async await
    wx.request({
      url: 'http://5990367be1e4470011c46fa8.mockapi.io/meng/user',
      success(res) {
       console.log(res)
      }
    })
  }
})

注意:

  1. 请求传递参数都是通过data传递
  2. 不支持Promise,通过回调success获取数据

使用注意

非法的请求域名

解决办法:

  1. 通过添加合法域名:满足=》支持https协议已经备案
  • 打开小程序管理后台=》开发=〉开发设置

  • 在=》服务器域名=〉点击开始配置

  1. 在开发者工具=》详情=〉本地设置,关闭校验

⚠️注意:适用于开发、测试阶段

常用API

本节目标: 小程序常用API方法

  • API 是小程序开发的核心,通过 API 开发者可以获得微信底层封装的高级特性,如网络请求、设备信息、本地存储等。
  • 小程序的 API 以全局对象 wx 为命名空间,格式为 wx.方法名() ,可全局调用。

扩展阅读:api文档(查阅)

界面

  1. wx.showToast 提示框
  2. wx.showModal 确认框
  3. wx.showTabBar 显示 tabBar
  4. wx.hideTabBar 隐藏 tabBar
wx.showToast({
  title: '成功',
  icon: 'success',
  duration: 2000
})

页面跳转

  1. wx.navigateTo(Object object)

保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。使用 wx.navigateBack 可以返回到原页面。

  1. wx.switchTab(Object object)

跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面

  1. wx.redirectTo(Object object)

关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。

// 跳转
wx.navigateTo({
    url: '/pages/logs/logs'
})

数据缓存 (类似localStorage)

  • wx.setStorage(Object object) 存储=> 如果存储复杂类型,不需要序列化字符串=》直接存
  • wx.getStorage(Object object) 获取
  • wx.removeStorage(Object object) 删除

注意⚠️:

  1. 单个 key 允许存储的最大数据长度为 1MB,所有数据存储上限为 10MB。
  2. 除非用户主动删除或因存储空间原因被系统清理,否则数据都一直可用
  3. 支持Promise
wx.setStorage({
  key:"key",
  data:"value"
}) 
// localStorage.setItem('xxx', 值)