微信开发工具常用快捷键

  • Ctrl + D: 选中匹配
  • Shift + ↑/↓: 移动一行代码
  • Shift + Alt + ↑/↓: 向上或下复制一行代码
  • Shift + Alt + F: 代码格式化
  • 输入一个标签单词,然后按 tab 可自动补全
  • 输入 api 前缀,然后按 tab 往往可以补全常用参数

新建页面的方法

在 app.json 文件下 pages 数组的页面路径,如果指向一个不存在的页面,那么 MINA 框架会自动创建这个页面的 4 个文件。
利用这点,我们可以快速创建页面且不会出 bug

相对路径与绝对路径

  • '/': '/'开头代表根目录,常用在 images 路径设置。比如 <image src="/images/icon/wx_app_like.png"></image>
  • ‘./’: './' 表示当前目录,可以省略。比如 './childIndex1/child' 可以写成 'childIndex1/child'
  • wx.navigateTo 的页面路径,采用相对路径,且不要加上文件扩展名。比如 wx.navigateTo({url: '../post/post'})
  • template 引用模板,采用相对路径,且需要文件扩展名。比如 <template is="postItem/postItem.wxml">
  • require 引用 js 模块,采用相对路径,且需要文件扩展名 js。(注意不可以用绝对路径,也不可以不加 .js)。比如 require('../../data/data.js')
  • tab 选项卡,pagePath, iconPath, selectedIconPath 采用绝对路径,不需扩展名。比如 "pagePath": "pages/post/post"。(注意不要用 '/' 开头)

小程序尺寸与 rpx 含义

官方建议以 iPhone6 作为标准做设计图。它的特点是:

  • 宽度 750rpx
  • 设计图里 1rpx = .5px
    只记结论的话:rpx 是相对单位,px 是长度单位。在 iphone6 设计稿里,1p = 2r

如何读懂模拟器的分辨率选项?
先介绍一些概念:
逻辑像素:px
物理像素:rpx(PS 设计图里的像素)
比如选择一种机型,给出的分辨率信息是: 375x667; Dpr: 2
意思是:屏幕宽 375px, 高 667px,其中 1px = 2rpx

全局样式文件 app.wxss

app 为名的文件都与全局有关
app.wxss 用途,比如说需要设置整个小程序的默认字体,最有效的做法是:

text {
  font-family: Microsoft YaHei;
}

学习 API 的方法

就是一个字,“试”
比如 window 这个配置项,只需将其他几个属性加入到 window 中,再更改几个可能的属性值,就可以立刻预览属性值效果。通过实践+观察就能掌握。
就像掌握一个不熟悉的软件或上手一个新 App, 所有按钮和选项多点多试就熟悉了。

小程序的 Boolean 坑

有这样一个问题,配置一个真值属性,比如 swiper 组件的 vertical 值,当我们写

<swiper vertical="false">
  ...
</swiper>

时,其实 vertical 的值是 true。不仅如此,只要字符串非空,该属性一直为真。设置 vertical = "aa", vertical = "bb" 都一样。
正确写法是:

<swiper vertical="{{false}}">
  ...
</swiper>

小程序里没有标签,只有组件

比如最基本的 <view>, <text>, <image>
比如扩展的 <swiper>,<input>
比起 html 各种杂七杂八的标签,可以说是相当简洁

一般来说都是双标签,有一些例外比如

  • <input />
  • <import />

image 组件的 4 种缩放模式和 9 种裁剪模式

设置 image 组件的 mode 属性即可
缩放模式

  • scaleToFill: 不保持横纵比例强行拉伸,直至填满
  • aspectFill: 使长边完全显示(也就是说可以完整显示全图,但是会有黑边)
  • aspectFit: 使短边完全显示(也就是说没有黑边,但是不会完整显示全图)
  • widthFit: 宽度不变,高度自动变化
    裁剪模式

直观讲,以容器尺寸为裁剪框,hover 图片。裁剪框有 9 种可选位置

  • top
  • bottom
  • center
  • left
  • right
  • top left
  • top right
  • bottom left
  • bottom right

裁剪模式虽然多达 9 种,却不难记忆,它们的位置可以用 “米” 字来想象。假设桌面上一张图片,用相框 hover 上去。
“米” 字的 “十” 部分代表相框位于上下左右中 (top, bottom, left, right, center)
“米” 字的叉部分代表相框位于四个对角(top left, top right, bottom left, bottom right)

navigateTo, redirectTo

区别:

  • navigateTo 将保留当前页面,然后跳转
  • redirectTo 将关闭当前页面,然后跳转
    直观体验,就是 navigateTo 跳转后页面可以返回,redirectTo 没有后退按钮

注意一点:
onHide 称为隐藏页面,onUnload 称为卸载页面
navigateTo 触发原页面的 onHide 不触发 onUnload
redirectTo 触发原页面的 onUnload 不触发 onHide
很容易理解,如果是为了节省内存,我也会这么设计

顺便一提:
A 页面 navigateTo B 页面,那么称 A 为父页面,B 为子页面
A 跳到 B,然后 B 点返回跳到 A,这时会触发 B 页面的 onUnload 函数,也就是说会卸载子页面

应用场景就是子页面上的音乐播放,如果子页面上点了一首歌,然后返回父页面,这时音乐会停止。解决办法就是用 playBackgroundAudio 全局播放

小程序的事件冒泡

结论是:catchXX 不冒泡,bindXX 冒泡

模板

意义是减少重复的 wxml 和 wxss 代码,当发现模式重复的时候就可用
三部曲:
第一步,新建文件夹,创建 wxml 和 wxss 文件
第二步,编写模板
第三步,引用模板

编写模板的格式是:

<template name="模板名">
  <view>...</view>
</template>

引用模板的格式是:

<import src="模板路径">
...
<template is="模板" data="{{传入模板数据}}">

注意模板路径采用相对路径,且需要文件扩展名。比如 <template is="postItem/postItem.wxml">

关于模板使用,有一个 trick,叫 "消除模板对外部变量名的依赖"
举个例子,如果我们引用页写入 <template is="xx" data="{{item}}">
那么模板页所有数据绑定都必须 {{item.xx}}, {{item.yy}}
每次写模板都要想传入参数名字并且与调用接口遵守约定太蠢了,一个好办法是用展开运算符 ...
还是那个例子,我们这样写 <template is="xx" data="{{...item}}">
模板页所有数据绑定直接 {{xx}}, {{yy}} 就行了
最关键的一点,现在我们引用页传入数据的参数可以随便改。 A 页写 data="{{...item1}}" ,B 页写 data="{{...item2}}" 都可以,这增加了模板的泛用性

小程序的异步方法

几乎所有小程序的异步 API 方法都包含这 3 个方法: success, fail, complete
比如 wx.setStorage(object)

编写缓存数据库类

缓存,和数据库是两个概念。
缓存就是开发者工具的 Storage,目的一是提高页面性能,二是在没有后端接口的时候 mock 接口数据
数据库类是非常关键的一块,意义在于使我们页面涉及后端的所有操作能用一句话完成,简洁高效地完成 MVVM 的 "更新 Model" 任务
缓存数据库,指的是建立在开发者工具 Storage 上的数据库

class DBPost {
  constructor(url) {
    this.storageKeyName = 'key'
  }
  // 获取全部数据
  getAllData() {
    let res = wx.getStorageSync(this.storageKeyName)
    if(!res) {
      // ../data/data.js 是预先准备的数据文件,在没有缓存的时候初始化数据
      let res = require('../data/data.js').postList
      wx.setStorageSync(this.storageKeyName, res)
    }
    return res
  }
}

export { DBPost }

具体到文章数据库,因为有 "根据 id 号获取文章详情","点赞","收藏" 等,为了方便增删改查如下:

class DBPost {
  constructor(url, itemId) {
    this.storageKeyName = 'key'
    this.itemId = itemId
  }
  // 查:获取全部数据
  getAllData() {}

  // 查:根据 id 获取文章详情
  getItemById() {
    let allData = this.getAllData()
    for(let i=0; i<allData.length; i++) {
      if(this.itemId === allData[i].id) {
        return {
          idx: i,
          data: allData[i]
        }
      }
    }
  }

  // 改:根据 id 修改收藏,点赞,阅读,评论数据
  updateItemById() {
    // 获取必要数据
    let allData = this.getAllData()
    let item = this.getItemById()
    let {data, idx} = item

    data.xxxx 属性修改
    data.yyyy 属性修改

    // 更新缓存数据库
    allData[idx] = data
    wx.setStorageSync(this.storageKeyName, allData)
    return allData
  }
}

不要在 template 上注册事件

<template catchtap="onTapToDetail" is="postItemTpl" data="{{...item}}"> (x)
<view catchtap="onTapToDetail"><template is="postItemTpl" data="{{...item}}"></view> (√)

原因是 template 标签是一个占位符,编译后会被它的内容取代从而在 wxml 中不可见

页面中传递参数的 3 种方式

页面间的通信,有以下几种参数传递方式:

  1. 使用全局变量(app.js)
  2. 使用缓存
  3. 通过页面导航 url 的 query 参数

即:url,缓存,全局变量

获取页面参数取值

通过页面里 onload 函数的 options 参数来获取

设置编译后的初始化页面

这是 IDE 的一个便利功能,一般编译后直接进入小程序首页(pages 数组第一项)
可以通过编译选项修改

收藏,点赞,评论功能通用套路

跟 vue 一样

onCollectionTap(event) {
  // 更新 M
  // 更新 VM
}

例子

onCollectionTap(event) {
  // 更新 M(一句话完成,如果不是,就修改缓存数据库类直到是)
  let newData = this.dbPost.collect()
  // 更新 VM
  this.setData({
    post.collectionStatus: newData.collectionStatus,
    post.collectionNum: newData.collectionNum
  })
}

本地缓存的重要性

提供本地的 key&value 缓存机制是小程序的一大特点,善用本地缓存可以极大地改善客户端体验与服务器性能。
在一个高性能产品中,缓存的重要性是不言而喻的。建议将本地缓存视作一个 key&value 数据库,并封装一些类和公共方法,提供给项目的各个调用方。最好不要让 getStorage, setStorage 充斥各角落

util 文件夹

新建 util 文件夹及 util.js,在上面编写公共方法

  • 在 Date 原型链上添加 format 方法
  • 日期计算
    ......

input 事件

  • bindinput
  • bindconfirm
  • bindfocus
  • bindblur

注意这四个 bind 事件不同于之前的 catch/bind规律,它们是非冒泡的

在 MVVM 框架实现 CSS3 动画

简单说,就是通过 viewModel 控制类的添加与删除

<view class="file {{deleteIdx===idx? 'deleting': ''}}"></view>

解决滑动卡顿问题

.xx {
  -webkit-overflow-scrolling: touch;
}

跳转到 tab选项卡的坑

需要特别注意的是,wx.redirectTo 和 wx.navigateTo 无法跳转到带 tab 选项卡的页面
只能用 wx.switchTab 跳转
同理,wx.switchTab 也无法跳转到不带 tab 选项卡的页面

实现页面下拉刷新的 "三部曲"

第一步:在页面的 json 文件中配置 enablePullDownRefresh
第二步:在页面的 js 文件中编写 onPullDownRefresh 函数
第三步:在回调函数中调用 wx.stopPullDownRefresh 函数

json 中的 backgroundColor 配置的是哪里的颜色

真机小程序,向下拉动后,在导航栏和页面间会出现一块空白,backgroundColor 定义这块的颜色

上滑加载更多数据

onReachBottom
注意这里要使用数据绑定的思维
追加 (x)
重新绑定和渲染整个数据数组 (√)

let movies = []
movies = this.data.movies.concat(newMovies)
this.setData({
  movies
})

动态设置导航栏 loading 图标

loading 可以给用户心理上一种等待中,将会获得数据的安心感。
我们无需写代码,直接用 api 即可
wx.showNavigationBarLoading()
wx.hideNavigationBarLoading()
注意在 onload 中调用该 api 是有风险的,官方文档明确说明应在 onReady 页面生命周期之后再操作界面元素。所以最好还是在 onReady 函数中设置 loading 状态