loading

微信小程序基础

# 微信小程序

# 1.计量单位

# 1.1 rpx

小程序的视图是根据iPhone6设计的,rpx单位是微信小程序中CSS的尺寸单位,rpx可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素,使用rpx可以使元素相对于屏幕的大小做出自适应

# 1.2 vw与vh

vw与vh也是相对于整个屏幕的宽高的CSS尺寸单位,1vw或1vh是相对于整个屏幕宽度或高度的百分之一,如设置width:100vw就是让元素的宽度占满整个屏幕,使用vw和vh也可以使元素相对于屏幕的大小做出自适应

# 2.配置

# 2.1 全局配置

小程序总界面的app.json文件用于配置全局的配置(必写),内部用一个{}进行总体包裹,主要的配置属性有:

**注:**每个页面会默认先加载全局配置再加载自身的配置,所以全局配置能控制所有页面的配置项

属性 类型 必填 描述
pages string[] 页面路径列表
window Object 全局的默认窗口表现
tabBar Object 底部 tab 栏的表现
networkTimeout Object 网络超时时间
debug boolean 是否开启 debug 模式,默认关闭
functionalPages boolean 是否启用插件功能页,默认关闭
subpackages Object[] 分包结构配置
workers string Worker代码放置的目录
requiredBackgroundModes string[] 需要在后台使用的能力,如「音乐播放」
plugins Object 使用到的插件
preloadRule Object 分包预下载规则
resizable boolean iPad 小程序是否支持屏幕旋转,默认关闭
navigateToMiniProgramAppIdList string[] 需要跳转的小程序列表
usingComponents Object 全局自定义组件配置
permission Object 小程序接口权限相关设置

# 2.1.1 pages

该属性里的配置项用于指定小程序的页面组成,该配置项是一个数组,每一个成员都对应着一个页面,当写入具体的文件名后会自动创建对应页面的相关文件

**注意:**文件名不需要写文件后缀,框架会自动去寻找对于位置的 .json, .js, .wxml, .wxss 四个文件进行处理

/*
当开发的目录为:
├── app.js
├── app.json
├── app.wxss
├── pages(主要看这里)
│   │── index
│   │   ├── index.wxml
│   │   ├── index.js
│   │   ├── index.json
│   │   └── index.wxss
│   └── logs
│       ├── logs.wxml
│       └── logs.js
└── utils
需要在pages配置项中写入如下的文件目录结构
*/
{
  "pages": ["pages/index/index", "pages/logs/logs"]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.1.2 window

该配置项用于设置小程序的状态栏、导航条、标题、窗口背景色等的状态和样式

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如 #000000
navigationBarTextStyle string white 导航栏标题颜色,仅支持 black / white
navigationBarTitleText string 导航栏标题文字内容
navigationStyle string default 导航栏样式,仅支持以下值: default 默认样式 custom 自定义导航栏,只保留右上角胶囊按钮。参见注2。
backgroundColor HexColor #ffffff 窗口的背景色
backgroundTextStyle string dark 下拉 loading 的样式,仅支持 dark / light
backgroundColorTop string #ffffff 顶部窗口的背景色,仅 iOS 支持
backgroundColorBottom string #ffffff 底部窗口的背景色,仅 iOS 支持
enablePullDownRefresh boolean false 是否开启全局的下拉刷新
onReachBottomDistance number 50 页面上拉触底事件触发时距页面底部距离,单位为px
pageOrientation string portrait 屏幕旋转设置,支持 auto / portrait / landscape
//app.json
{
  "window": {
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "微信接口功能演示",
    "backgroundColor": "#eeeeee",
    "backgroundTextStyle": "light"
  }
}
1
2
3
4
5
6
7
8
9
10

# 2.1.3 tabBar

该配置项指定底部(可切换至顶部)tab 栏的表现,以及 tab 切换时显示的对应页面

属性 类型 必填 默认值 描述
color HexColor tab 上的文字默认颜色,仅支持十六进制颜色
selectedColor HexColor tab 上的文字选中时的颜色,仅支持十六进制颜色
backgroundColor HexColor tab 的背景色,仅支持十六进制颜色
borderStyle string black tabbar上边框的颜色, 仅支持 black / white
list Array tab 的列表,详见 list 属性说明,最少2个、最多5个 tab
position string bottom tabBar的位置,仅支持 bottom / top
custom boolean false 自定义 tabBar

list的值为一个数组,只能配置最少 2 个、最多 5 个 tab。tab 按数组的顺序排序,每个项都是一个对象,其属性值如下:

属性 类型 必填 说明
pagePath string 页面路径,必须在 pages 中先定义,点击能跳转到指定的页面
text string tab 上按钮文字
iconPath string 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,不支持网络图片。 当 postion 为 top 时,不显示 icon。
selectedIconPath string 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,不支持网络图片。 当 postion 为 top 时,不显示 icon。

# 2.2 页面配置

每个页面也有一个.json文件,在这个文件中可以配置只属于当前页面的属性

注意:

  • 因为只用表现当前的页面窗口状态,所以页面中配置项在当前页面仅会覆盖 app.jsonwindow 中相同的配置项

  • 页面配置中只能设置 app.jsonwindow 对应的配置项,以决定本页面的窗口表现,所以无需写 window 这个属性

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如 #000000
navigationBarTextStyle string white 导航栏标题颜色,仅支持 black / white
navigationBarTitleText string 导航栏标题文字内容
navigationStyle string default 导航栏样式,仅支持以下值: default 默认样式 custom 自定义导航栏,只保留右上角胶囊按钮
backgroundColor HexColor #ffffff 窗口的背景色
backgroundTextStyle string dark 下拉 loading 的样式,仅支持 dark / light
backgroundColorTop string #ffffff 顶部窗口的背景色,仅 iOS 支持
backgroundColorBottom string #ffffff 底部窗口的背景色,仅 iOS 支持
enablePullDownRefresh boolean false 是否开启当前页面下拉刷新
onReachBottomDistance number 50 页面上拉触底事件触发时距页面底部距离,单位为px。 详见
pageOrientation string portrait 屏幕旋转设置,支持 auto / portrait / landscape 详见
disableScroll boolean false 设置为 true 则页面整体不能上下滚动。 只在页面配置中有效,无法在 app.json 中设置
disableSwipeBack boolean false 禁止页面右滑手势返回
usingComponents Object 页面自定义组件配置
//当前页面的.json文件中
{
  "navigationBarBackgroundColor": "#ffffff",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "微信接口功能演示",
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
}
1
2
3
4
5
6
7
8

# 2.3 工具配置

小程序开发者工具在每个项目的根目录都会生成一个 project.config.json在工具上做的任何配置都会写入到这个文件,当重新安装工具或者换电脑工作时,你只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时你开发项目时的个性化配置,其中会包括编辑器的颜色、代码上传时自动压缩等等一系列选项

# 2.4 全局样式与局部样式

小程序提供了全局的样式和局部样式。和前边 app.json, page.json 的概念相同,可以写一个 app.wxss 作为全局样式,会作用于当前小程序的所有页面,而局部页面样式 page.wxss 仅对当前页面生效

注意:WXSS 仅支持部分CSS 选择器

选择器 样例 样例描述
.class .intro 选择所有拥有 class="intro" 的组件
#id #firstname 选择拥有 id="firstname" 的组件
element view 选择所有 view 组件
element, element view, checkbox 选择所有文档的 view 组件和所有的 checkbox 组件
::after view::after 在 view 组件后边插入内容
::before view::before 在 view 组件前边插入内容

# 3.小程序生命周期

一个小程序有一个代表整个小程序的全局app.js文件,每个小程序都需要在该文件中调用App()方法注册一个小程序示例,绑定生命周期回调函数、错误监听和页面不存在监听函数等

属性 类型 必填 说明
onLaunch function 生命周期回调——监听小程序初始化。
onShow function 生命周期回调——监听小程序启动或切前台。
onHide function 生命周期回调——监听小程序切后台。
onError function 错误监听函数。
onPageNotFound function 页面不存在监听函数,用法同与 wx.onPageNotFound
其他 any 开发者可以添加任意的函数或数据变量到 Object 参数中,用 this 可以访问
// app.js
//App里面的生命周期函数是全局的
App({
    //小程序初始化,可以接受一个参数,记录了页面信息,还有用户是通过哪种形式访问的小程序
    onLaunch(options) {
        // Do something initial when launch.
    },
    //小程序第一次页面内容显示的时候出发,有可能多次触发
    onShow(options) {
        // Do something when show.
    },
    //小程序页面切换到后台时触发,可能重复触发
    onHide() {
        // Do something when hide.
    },
    //日志功能,监听小程序发生错误
    onError(msg) {
        //分析运行状态
        console.log(msg)
    },
    onPageNotFound(res) {
        wx.redirectTo({
            url: 'pages/...'
        }) // 如果是 tabbar 页面,请使用 wx.switchTab
    },
    //可以设置全局的数据,通过getApp()方法可以在任何页面获取这个数据
    globalData: 'I am global data'
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//可以在其余页面的js文件中通过getApp()方法获取到这个App示例
// xxx.js
const appInstance = getApp()
console.log(appInstance.globalData) // I am global data
1
2
3
4

# 4.页面生命周期

对于小程序中的每个页面来说,都需要在页面对应的 .js 文件中调用 Page()方法注册页面示例,指定页面的初始数据、生命周期回调、事件处理函数等

**注意:**子页面最多只能嵌套5个,多余的页面会从第一个开始卸载

属性 类型 说明
data Object 页面的初始数据
onLoad function 生命周期回调—监听页面加载
onReady function 生命周期回调—监听页面初次渲染完成
onShow function 生命周期回调—监听页面显示
onHide function 生命周期回调—监听页面隐藏
onUnload function 生命周期回调—监听页面卸载
onPullDownRefresh function 监听用户下拉动作
onReachBottom function 页面上拉触底事件的处理函数
onShareAppMessage function 用户点击右上角转发
onPageScroll function 页面滚动触发事件的处理函数
onResize function 页面尺寸改变时触发
onTabItemTap function 当前是 tab 页时,点击 tab 时触发
其他 any 开发者可以添加任意的函数或数据到 Object 参数中,在页面的函数中用 this 可以访问
Page({

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

    },

    /**
   * 生命周期函数--监听页面加载
   */
    onLoad: function (options) {
        console.log("页面开始加载,页面初次进入白屏时在这里请求数据");
    },

    /**
   * 生命周期函数--监听页面初次渲染完成
   */
    onReady: function () {
        console.log("页面渲染完成,用户可以进行相关操作");
    },

    /**
   * 生命周期函数--监听页面显示
   */
    onShow: function () {
        console.log("页面显示");
    },

    /**
   * 生命周期函数--监听页面隐藏
   */
    onHide: function () {
        console.log("用户打开了子页面或者切换了后台");
    },

    /**
   * 生命周期函数--监听页面卸载
   */
    onUnload: function () {
        console.log("页面跳转或者子页面超过5个被强制卸载了");
    },

    /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
    onPullDownRefresh: function () {
        console.log("下拉刷新,重新请求数据");
    },

    /**
   * 页面上拉触底事件的处理函数
   */
    onReachBottom: function () {
        console.log("滚动到底部");
    },

    /**
   * 用户点击右上角分享
   */
    onShareAppMessage: function () {
        console.log("用户点击了分享按钮");
    },
    /*
    页面滚动
    */
    onPageScroll() {
        // Do something when page scroll
    },
    /*
    页面大小发生改变
    */
    onResize() {
        // Do something when page resize
    },
    /*
    点击tab时触发
    */
    onTabItemTap(item) {
    console.log(item.index);//被点击tabItem的序号,从0开始
    console.log(item.pagePath);//被点击tabItem的页面路径
    console.log(item.text);//被点击tabItem的按钮文字
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

# 4.1 data

data 是页面第一次渲染使用的初始数据,页面加载时,data 将会以JSON字符串的形式由逻辑层传至渲染层,因此**data中的数据必须是可以转成JSON的类型:字符串,数字,布尔值,对象,数组**,在wxml中可以通过字符串模板{{}}进行数据绑定

**注意:**data中的数据不能是一个函数

<view>{{text}}</view><!--view标签相当于div标签-->
<view>{{array[0].msg}}</view>
1
2
Page({
  data: {
    text: 'init data',
    array: [{msg: '1'}, {msg: '2'}]
  }
})
1
2
3
4
5
6

# 4.2 setData方法

由于data中的数据只是初始时的数据,只有在第一次进入页面时才会进行渲染,所以不能够通过直接改变data中的值来动态改变页面中的数据,但是可以通过setData()方法实现动态的改变并重新渲染data

setData()方法可以接受两个参数

  • 一个对象(必填),对象中是要改变的数据,该函数会根据对象中对应的值对data中的数据进行修改
  • callback回调函数,setData()方法引起的界面更新渲染完毕后的回调函数

注意:

  • 直接修改 this.data 而不调用 this.setData() 是无法改变页面的状态的,还会造成数据不一致
  • 仅支持设置可 JSON 化的数据
  • 单次设置的数据不能超过1024kB,请尽量避免一次设置过多的数据,所以最好只修改要修改的部分
  • 不要把 data 中任何一项的 value 设为 undefined ,否则这一项将不被设置并可能遗留一些潜在问题
<!--index.wxml-->
<view>{{text}}</view>
<button bindtap="changeText">Change normal data</button>
<view>{{num}}</view>
<button bindtap="changeNum">Change normal num</button>
<view>{{array[0].text}}</view>
<button bindtap="changeItemInArray">Change Array data</button>
<view>{{object.text}}</view>
<button bindtap="changeItemInObject">Change Object data</button>
<view>{{newField.text}}</view>
<button bindtap="addNewField">Add new data</button>
1
2
3
4
5
6
7
8
9
10
11
// index.js
Page({
  data: {
    text: 'init data',
    num: 0,
    array: [{text: 'init data'}],
    object: {
      text: 'init data'
    }
  },
  changeText() {
    // this.data.text = 'changed data' // 不要直接修改 this.data
    // 应该使用 setData
    this.setData({
      text: 'changed data'
    })
  },
  changeNum() {
    // 或者,可以修改 this.data 之后马上用 setData 设置一下修改了的字段
    this.data.num = 1
    this.setData({
      num: this.data.num
    })
  },
  changeItemInArray() {
    // 对于对象或数组字段,可以直接修改一个其下的子字段,这样做通常比修改整个对象或数组更好
    this.setData({
      'array[0].text': 'changed data'
    })
  },
  changeItemInObject() {
    this.setData({
      'object.text': 'changed data'
    })
  },
  addNewField() {
    this.setData({
      'newField.text': 'new data'
    })
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 4.3 getCurrentPages方法

**该方法可以获取当前页面栈的数组,数组中第一个元素为首页,最后一个元素为当前页面。**该方法常用于在某个页面修改另一个页面的data或者调用另一个页面的方法

Page({
chooseLocation:function(e){    
    var pages = getCurrentPages();    
    var prevPage = pages[pages.length - 2];//当前页面的上一个页面    
    /*
    pages[pages.length - 1]当前页面
    pages[0]首页
    */
    prevPage.setData({          
        'add.pcode': e.target.dataset.pcode,          
        'add.citycode': e.target.dataset.citycode,          
        'add.adcode': e.target.dataset.adcode,          
        'add.address': e.target.dataset.name,          
        'add.lng': e.target.dataset.latlng.split(',')[0],          
        'add.lat': e.target.dataset.latlng.split(',')[1],    
    });      
    wx.navigateBack({
        delta: 1   
    })}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 5.数据绑定

# 5.1 使用数据

*简单的数据使用与在标签的属性中引用要使用的属性是通过模板{{}}进行的,如果在想要引入的数据的字符串前后用了{{}},小程序在解析时就会对该字符串进行解析,而不是单纯的作为字符串使用

**注意:**除了字符串,其他的所有数据类型如果要使用其原本的特性都需要通过在外边加上{{}}来使用,否则都会被当作字符串解析

# 5.2 运算

{{}}中的内容可以进行简单的逻辑运算,包括:

  • 三元运算
  • 算术运算
  • 逻辑判断
  • 字符串运算
  • 数据路径运算

# 5.3 组合

  • 可以直接在模板{{}}内组合构成一个数组或者对象

  • 数组

    如果要组合成数组需要在内部额外的用[]进行包裹

    <view wx:for="{{[zero, 1, 2, 3, 4]}}" wx:for-item='item'>{{item}}</view>
    
    1
    Page({
      data: {
        zero: 0
      }
    })
    /*
    最终组合成数组[0, 1, 2, 3, 4]
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 对象

    如果要组合成对象只需要在{{}}内部写入对象需要的属性就可以了

    <template is="objectCombine" data="{{for: a, bar: b}}"></template>
    
    1
    Page({
      data: {
        a: 1,
        b: 2
      }
    })
    /*
    最终组合成的对象是 {for: 1, bar: 2}
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    注意:

    • 可以使用拓展运算符将一个对象展开进行传值

      <template is="objectCombine" data="{{...obj1, ...obj2, e: 5}}"></template>
      
      1
      Page({
        data: {
          obj1: {
            a: 1,
            b: 2
          },
          obj2: {
            c: 3,
            d: 4
          }
        }
      })
      /*
      最终组合成的对象是 {a: 1, b: 2, c: 3, d: 4, e: 5}
      */
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
    • 对象的key和value相同也可以只写一个

    • 上述方式可以随意组合,但是如有存在变量名相同的情况,后边的会覆盖前面

    • 花括号和引号之间如果有空格,将最终被解析成为字符串

      <view wx:for="{{[1,2,3]}}" wx:for-item='item'>
        {{item}}
      </view>
      <!--等同于-->
      <view wx:for="{{[1,2,3] + ' '}}" wx:for-item='item'>
        {{item}}
      </view>
      
      1
      2
      3
      4
      5
      6
      7

# 6.列表渲染

# 6.1 用法

在组件上使用wx:for能够绑定一个数组,从而使用数组中的各项来渲染

<view wx:for="{{array}}">
  {{index}}: {{item.message}}
</view>
1
2
3
Page({
  data: {
    array: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  }
})
1
2
3
4
5
6
7
8
9

**注意:**默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item,如果要改变它们,可以使用wx:for-itemwx:for-index对指定下标进行重命名

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

注:

  • wx:for 的值为字符串时,会将字符串解析成字符串数组

    <view wx:for="array">
      {{item}}
    </view>
    <!--等同于-->
    <view wx:for="{{['a','r','r','a','y']}}">
      {{item}}
    </view>
    
    1
    2
    3
    4
    5
    6
    7
  • 花括号和引号之间如果有空格,将最终被解析成为字符串

    <view wx:for="{{[1,2,3]}} ">
      {{item}}
    </view>
    <!--等同于-->
    <view wx:for="{{[1,2,3] + ' '}}">
      {{item}}
    </view>
    
    1
    2
    3
    4
    5
    6
    7

# 6.2 block

可以将 wx:for 用在<block/>标签上,以渲染一个包含多节点的结构块,block标签不会被解析出来

<block wx:for="{{[1, 2, 3]}}">
  <view>{{index}}:</view>
  <view>{{item}}</view>
</block>
1
2
3
4

注意: <block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性

# 6.3 key

在列表循环中使用wx:key来指定列表中项目的唯一的标识符,当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保它们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率

wx:key值的两种形式提供

  • 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变

  • 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字

    <switch wx:for="{{objectArray}}" wx:key="unique" style="display: block;">
      {{item.id}}
    </switch>
    <button bindtap="switch">Switch</button>
    <button bindtap="addToFront">Add to the front</button>
    
    <switch wx:for="{{numberArray}}" wx:key="*this" style="display: block;">
      {{item}}
    </switch>
    <button bindtap="addNumberToFront">Add to the front</button>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Page({
      data: {
        objectArray: [
          {id: 5, unique: 'unique_5'},
          {id: 4, unique: 'unique_4'},
          {id: 3, unique: 'unique_3'},
          {id: 2, unique: 'unique_2'},
          {id: 1, unique: 'unique_1'},
          {id: 0, unique: 'unique_0'},
        ],
        numberArray: [1, 2, 3, 4]
      },
      switch(e) {
        const length = this.data.objectArray.length
        for (let i = 0; i < length; ++i) {
          const x = Math.floor(Math.random() * length)
          const y = Math.floor(Math.random() * length)
          const temp = this.data.objectArray[x]
          this.data.objectArray[x] = this.data.objectArray[y]
          this.data.objectArray[y] = temp
        }
        this.setData({
          objectArray: this.data.objectArray
        })
      },
      addToFront(e) {
        const length = this.data.objectArray.length
        this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray)
        this.setData({
          objectArray: this.data.objectArray
        })
      },
      addNumberToFront(e) {
        this.data.numberArray = [this.data.numberArray.length + 1].concat(this.data.numberArray)
        this.setData({
          numberArray: this.data.numberArray
        })
      }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39

# 7.条件渲染

在框架中使用wx:if来判断是否需要渲染代码,使用hidden来判断是否显示该代码(依旧要渲染代码,只是display为none)

  • wx:if

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

    也可以用 wx:elifwx:else 来添加一个 else 块

    <view wx:if="{{length > 5}}">1</view>
    <view wx:elif="{{length > 2}}">2</view>
    <view wx:else>3</view>
    
    1
    2
    3

    也可以使用block标签控制多个元素

    <block wx:if="{{true}}">
      <view>view1</view>
      <view>view2</view>
    </block>
    
    1
    2
    3
    4
  • hidden

    <view hidden="true">
        <text>text1</text>
        <text>text2</text>
    </view>
    
    1
    2
    3
    4

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好

# 8.组件模板

# 8.1 定义模板

使用 name 属性,作为模板的名字。然后在<template/>内定义代码片段

<template name="msgItem">
  <view>
    <text>{{index}}: {{msg}}</text>
    <text>Time: {{time}}</text>
  </view>
</template>
1
2
3
4
5
6

# 8.2 使用模板

使用 is 属性,声明需要的使用的模板,然后将模板所需要的data传入,就能在模板中显示data中的信息

<template is="msgItem" data="{{...item}}" />
1
Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    }
  }
})
1
2
3
4
5
6
7
8
9

is 属性可以使用{{}}来动态决定具体需要渲染哪个模板

<template name="odd">
  <view>odd</view>
</template>
<template name="even">
  <view>even</view>
</template>

<block wx:for="{{[1, 2, 3, 4, 5]}}">
  <template is="{{item % 2 == 0 ? 'even' : 'odd'}}" />
</block>
1
2
3
4
5
6
7
8
9
10

**注意:**模板有着的作用域,在于上级作用域相互矛盾时会先使用自身作用域的值

# 8.3 模板传值

将data中的值传递给子组件时可以使用两种方法

  • 在data中直接传入对象后在模板中使用和传入数据一样的名字来使用传入的数据

    <template name="list">
    <view class="list">
    <temlate is="item" wx:for="{imgs}" data="{{item}}"></temlate>    
    </view>
    </template>
    
    <template name="item">
    <view class="item">
    <image src="{{item.src}}"></image>
    </view>
    </template>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 在传入数据的时候使用解构的方式将传入的数据解构,然后就可以直接书写对象的属性来拿到对象中的值

    <template name="list">
    <view class="list">
    <temlate is="item" wx:for="{imgs}" data="{{...item}}"></temlate>    
    </view>
    </template>
    
    <template name="item">
    <view class="item">
    <image src="{{src}}"></image>
    </view>
    </template>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 9.引用

# 9.1 import

  • 使用import标签可以在该文件中使用目标文件定义的template

    <!-- item.wxml -->
    <template name="item">
      <text>{{text}}</text>
    </template>
    
    1
    2
    3
    4
    <!-- index.wxml -->
    <import src="item.wxml" />
    <template is="item" data="{{text: 'forbar'}}" />
    
    1
    2
    3

    **注意:**import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template

  • 使用@import能够引入其他组件的样式文件,和全局样式类似,写在后面的样式会覆盖前面的样式

    /*list.wxss*/
    .list{
        padding:20rpx;
    }
    .item image{
        width:100%;
        display:block;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    /*index.wxss*/
    @import "list.wxss";
    .list{
        padding:15rpx;
    }
    
    1
    2
    3
    4
    5

# 9.2 include

include 可以将目标文件除了 <template/><wxs/> 外的整个代码引入,相当于是拷贝到 include 位置

<!-- index.wxml -->
<include src="header.wxml" />
<view>body</view>
<include src="footer.wxml" />
1
2
3
4
<!-- header.wxml -->
<view>header</view>
1
2
<!-- footer.wxml -->
<view>footer</view>
1
2

**注:**改方法常常用于在每个页面引入相同的内容,如相同的导航,并且include是可以递归导入的,会将所有导入的都一一解析

# 10.WXS

WXS是一种简易快速的类似JS的脚本代码,可以编写在 wxml 文件中的 <wxs> 标签内,或以 .wxs 为后缀名的文件内,但是由于所支持的东西不多,只能用ES5的语法,同时还有很多限制,所以推荐只有简易运算的时候使用WXS

**注意:**每一个 .wxs 文件和 <wxs> 标签都是一个单独的模块,每个模块都有自己独立的作用域。即在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见,要想对外暴露其内部的私有变量与函数,只能通过 module.exports 实现

# 10.1 module属性

每个 wxs 模块均有一个内置的 module 对象,通过该对对象的exports属性进行赋值可以对外共享本模块的私有变量与函数

// /pages/tools.wxs

var foo = "'hello world' from tools.wxs";
var bar = function (d) {
  return d;
}
module.exports = {
  FOO: foo,
  bar: bar,
};
module.exports.msg = "some msg";
1
2
3
4
5
6
7
8
9
10
11
<!-- page/index/index.wxml -->

<wxs src="./../tools.wxs" module="tools" />
<view>{{tools.msg}}</view>
<view>{{tools.bar(tools.FOO)}}</view>
<!--
some msg
'hello world' from tools.wxs
-->
1
2
3
4
5
6
7
8
9

# 10.2 < wxs> 标签

<wxs>标签是用于在wxml页面中引用WXS,该标签上有两个重要属性

属性名 类型 说明
module String 当前 <wxs> 标签的模块名。必填字段。
src String 引用 .wxs 文件的相对路径。仅当本标签为单闭合标签标签的内容为空时有效。

# 10.2.1 module属性

module 属性是当前 <wxs> 标签的模块名。在单个 wxml 文件内,建议其值唯一。有重复模块名则按照先后顺序覆盖(后者覆盖前者)。不同文件之间的 wxs 模块名不会相互覆盖

注意:

  • module 属性值的命名首字符必须是:字母(a-zA-Z),下划线(_)
  • 剩余字符可以是:字母(a-zA-Z),下划线(_),数字(0-9)
<!--这种是在当前的wxml页面直接进行代码的书写-->
<wxs module="foo"><!--通过这种方式将foo模块的导出对象暴露出来并命名,就能在下方进行使用-->
  var some_msg = "hello world"; 
  module.exports = { msg : some_msg, }
</wxs>
<view>{{foo.msg}}</view>
<!--
hello world
-->
1
2
3
4
5
6
7
8
9

# 10.2.2 src属性

<wxs>标签的src 属性可以用来引用其他的 .wxs 文件模块,然后直接下wxml页面进行使用

注意:

  • 只能引用 .wxs 文件模块,且必须使用相对路径

  • wxs 模块均为单例,wxs 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 wxs 模块对象

  • 如果一个 wxs 模块在定义之后,一直没有被引用,则该模块不会被解析与运行

  • <wxs> 模块只能在定义模块的 WXML 文件中被访问到。使用 <include><import> 时,<wxs> 模块不会被引入到对应的 WXML 文件中

  • 由于作用域的问题,<template> 标签中,只能使用定义该 <template> 的 WXML 文件中定义的 <wxs> 模块,可以在定义<template>的WXML文件中引入<wxs>模块

// /pages/index/index.js

Page({
  data: {
    msg: "'hello wrold' from js",
  }
})
1
2
3
4
5
6
7
<!-- /pages/index/index.wxml -->

<wxs src="./../comm.wxs" module="some_comms"></wxs>
<!-- 也可以直接使用单标签闭合的写法
<wxs src="./../comm.wxs" module="some_comms" />
-->

<!-- 调用 some_comms 模块里面的 bar 函数,且参数为 some_comms 模块里面的 foo -->
<view>{{some_comms.bar(some_comms.foo)}}</view>
<!-- 调用 some_comms 模块里面的 bar 函数,且参数为 page/index/index.js 里面的 msg -->
<view>{{some_comms.bar(msg)}}</view>

<!--
'hello world' from comm.wxs
'hello wrold' from js
-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 10.3 require函数

如果想要在一个.wxs文件中引入其他的wxs模块,就需要使用require函数进行模块的导入

注意:

  • 只能引用 .wxs 文件模块,且必须使用相对路径。
  • wxs 模块均为单例,wxs 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 wxs 模块对象
  • 如果一个 wxs 模块在定义之后,一直没有被引用,则该模块不会被解析与运行
// /pages/tools.wxs

var foo = "'hello world' from tools.wxs";
var bar = function (d) {
  return d;
}
module.exports = {
  FOO: foo,
  bar: bar,
};
module.exports.msg = "some msg";
1
2
3
4
5
6
7
8
9
10
11
// /pages/logic.wxs
//与node一样,都是通过对象的赋值进行调用
var tools = require("./tools.wxs");

console.log(tools.FOO);
console.log(tools.bar("logic.wxs"));
console.log(tools.msg);
1
2
3
4
5
6
7
<!-- /page/index/index.wxml -->

<wxs src="./../logic.wxs" module="logic" />
<!--
'hello world' from tools.wxs
logic.wxs
some msg
-->
1
2
3
4
5
6
7
8

# 10.4 数据类型

WSX的语法中支持以下数据类型:

  • number : 数值
  • string :字符串
  • boolean:布尔值
  • object:对象
  • function:函数
  • array : 数组
  • date:日期
  • regexp:正则

**注意:**以上所有数据类型的API都可参考ES5标准,不能使用ES6的语法

# 10.4.1 data

生成 date 对象需要使用 getDate函数, 返回一个当前时间的对象

getData的参数:

  • milliseconds: 从1970年1月1日00:00:00 UTC开始计算的毫秒数
  • datestring: 日期字符串,其格式为:"month day, year hours:minutes:seconds"
var date = getDate(); //返回当前时间对象

date = getDate(1500000000000);
// Fri Jul 14 2017 10:40:00 GMT+0800 (中国标准时间)
date = getDate('2017-7-14');
// Fri Jul 14 2017 00:00:00 GMT+0800 (中国标准时间)
date = getDate(2017, 6, 14, 10, 40, 0, 0);
// Fri Jul 14 2017 10:40:00 GMT+0800 (中国标准时间)
1
2
3
4
5
6
7
8

# 10.4.2 regexp

生成 regexp 对象需要使用 getRegExp函数

getRegExp的参数:

  • pattern: 正则表达式的内容
  • flags:修饰符。该字段只能包含以下字符:
    • g: global
    • i: ignoreCase
    • m: multiline。
getRegExp(pattern[, flags])
1
var a = getRegExp("x", "img");
console.log("x" === a.source);
console.log(true === a.global);
console.log(true === a.ignoreCase);
console.log(true === a.multiline);
1
2
3
4
5

# 11.JS模块

由于WXS的语法缺陷,更倾向于直接导入JS文件放入全局中,但是这个JS文件不能直接使用,需要放在内置的函数App和Page函数内,所以如果只是要在页面简单使用工具函数WSX相对更方便

# 11.1 导出JS模块

和WXS一样,导出一个JS的模块文件时也需要使用module.exports或直接使用export关键字来导出一个对象,同时在js文件中支持ES6语法,能更好的进行代码操作

// /pages/tools.js

var foo = "'hello world' from tools.wxs";
var bar = function (d) {
    return d;
}
module.exports = {
    FOO: foo,
    bar: bar,
};
module.exports.msg = "some msg";
/*
export{
   FOO: foo,
   bar: bar,
   msg :"some msg"
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 11.2 导入JS模块

与node一样,如果要在一个模板中导入一个JS模板,也需要使用require()或import来导入

// /pages/index.js
var tools=require("./pages/tools.js");
/*
 或import tools from "./pages/tools.js";
*/
console.log(tools,Foo);
console.log(tools.msg);
1
2
3
4
5
6
7

**注:**可以在一个需要导入的模板中导入另外一个模板

# 11.3 JS公共模板

通过ES6的Object.assign()方法可以实现对象的合并从而将导入的一个JS模板作为公共的模板在页面全局进行使用

// /pages/index.js
var tools=require("./pages/tools.js");
Page(Object.assign({
    /**
   * 页面的初始数据
   */
    data: {

    }
},tools))
1
2
3
4
5
6
7
8
9
10

# 12.事件

# 12.1 事件的分类

事件分为冒泡事件和非冒泡事件:

  • 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递
  • 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递

WXML的冒泡事件列表:

类型 触发条件
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
touchend 手指触摸动作结束
tap 手指触摸后马上离开
longpress 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发
longtap 手指触摸后,超过350ms再离开(推荐使用longpress事件代替)
transitionend 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart 会在一个 WXSS animation 动画开始时触发
animationiteration 会在一个 WXSS animation 一次迭代结束时触发
animationend 会在一个 WXSS animation 动画完成时触发
touchforcechange 在支持 3D Touch 的 iPhone 设备,重按时会触发

# 12.2 事件的绑定

事件绑定的写法同组件的属性,以 key、value 的形式

  • key 以bindcatch开头,然后跟上事件的类型,如bindtapcatchtouchstart,在非原生组件中,bindcatch后可以紧跟一个冒号,其含义不变,如bind:tapcatch:touchstart
  • value 是一个字符串,需要在对应的 Page 中定义同名的函数。不然当触发事件的时候会报错。

bind与catch的区别

  • bind事件绑定不会阻止冒泡事件向上冒泡
  • catch事件绑定可以阻止冒泡事件向上冒泡
<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view
    <view id="inner" bindtap="handleTap3">
      inner view
    </view>
  </view>
</view>
1
2
3
4
5
6
7
8
9

捕获阶段

**在需要绑定事件的元素上用capture-bindcapture-catch绑定事件能够在捕获阶段就进行事件,**后者将中断捕获阶段和取消冒泡阶段

<view
  id="outer"
  bind:touchstart="handleTap1"
  capture-bind:touchstart="handleTap2"
>
  outer view
  <view
    id="inner"
    bind:touchstart="handleTap3"
    capture-bind:touchstart="handleTap4"
  >
    inner view
  </view>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 12.3 事件与App和Page封装

//Event.js
//对数组的类进行扩展
import myArray  from "./ArrayEX.js";

//事件的类
export default class Event{
  constructor(){
    //用来保存事件监听的类型和方法
    Object.defineProperty(this,"events",{
      value: {},
      enumerable: false
    });
  }

  //事件队列的触发器
  static createEventHandle(eType,that){
    //生成触发器的包装函数
    Reflect.set(that,eType,function(...args){

      //保存页面的this;
      const page=this;
      //事件队列方法,复制一个队列,防止只有一个执行的队列打乱顺序
      let eTypeFn = Array.from(Reflect.get(that.events, eType)),
      data=[];

      eTypeFn.forEach(fn => data.pushNameSpace(fn.apply(page,args)));
      // (function recur(){
      //   //每次出列第一个
      //   const f=eTypeFn.shift();
      //   //如果是一个方法就立刻执行
      //   f&&f.apply(page,args);
      //   //判断队列是否为空,不为空就继续执行
      //   eTypeFn.length&&recur();
      // })();
      return data;
    })
  }

  //获取事件队列
  getEvent(eType){
    let eTypeFn=Reflect.get(this.events,eType);
    if(!Array.isArray(eTypeFn)){
      eTypeFn=[];
      Reflect.set(this.events,eType,eTypeFn);
      //添加一个触发器
      Event.createEventHandle(eType,this);
    }
    return eTypeFn;
  }

  //添加一个事件监听
  addEvent(eType,callback){
    const eTypeFn=this.getEvent(eType);
    eTypeFn.push(callback);
  }

  //删除一个事件监听

  removeEvent(eType, callback){
    //带callback指定删除某个方法
    if(callback){
      let eTypeFn=this.getEvent(eType);
      let index=eTypeFn.findIndex(item=>item===callback);

      //因为删除行为时同步的,会造成不好的结果,所以改成异步执行,但是依旧有缺陷
      // index != -1 && setTimeout([].splice.bind(eTypeFn), 0, index, 1);
      index!=-1&&eTypeFn.splice(index,1);
    }else{  
      Reflect.set(this.events,eType,[]);
    }
  }

  //一次性事件
  oneEvent(eType,callback){
    const that=this;
    const handle=function(...args){
      callback.apply(this,args);
      that.removeEvent(eType,handle);
    };
    this.addEvent(eType, handle);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//ArrayEX.js
//打包命名空间参数
Array.prototype.pushNameSpace = function(...args) {
  args = args.map(item => {
    if (typeof item === "object") {
      //是否带了命名空间
      if (item.nameSpace) {
        return {
          nameSpace: item.nameSpace,
          data: item.data
        }
      } else {
        return {
          nameSpace: "default",
          data: item
        }
      }
    } else {
      return {
        nameSpace: "default",
        data: item
      }
    }
  });
  this.push(...args);
}

//查询命名空间参数
Array.prototype.findNameSpace = function(nameSpace = "default", subscript) {
  //查询当前命名空间的参数
  const data = this.filter(item => {
    return new RegExp(nameSpace, "i").test(item.nameSpace);
  }).map(item => item.data);

  if (typeof subscript === "boolean" && subscript) {
    return data;
  } else {

    //如果没有指定的下标,默认取最后一个
    if (subscript === undefined) {
      subscript = data.length - 1;
    }
    return data[subscript];
  }
}


export default Array;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//App.js
import Event from "./Event.js";
//因为App还没有被加载,所以不能通过getApp()方法获取到app实例
let app;
//公共数据的发送和保存
export default class AppModule extends Event {


  constructor() {
    super();
    //全局数据
    this.globalData = {};

    //页面数据
    this.pageData = {};
  }

  //给当前页面设置数据,不用在实际显示的页面显示数据,直接通过assign代理直接给当前页面设置
  assign(key,value){
    const page=app.page.page;
    const kType=typeof key;

    if(kType==="string"&&value!==undefined){
      page.setData({
        [key]:value
      });
    }else if(kType==="object"){
      page.setData(key);
    }
  }


  data(...arg) {
    //没有参数直接返回
    if(arg.length===0){
      return this.globalData;
    }else if(arg.length===1){
      //获取第一个参数类型
      const kType=typeof arg[0];
      //判断类型是否为字符串
      if(kType==="string"){

        //获取某一项
        return this.globalData[arg[0]];
      }
      if(kType==="object"){
        const data=arg[0];
        for(let key in data){
          this.data(key,data[key]);
        }
      }
    }else if(arg.length===2){
      this.globalData[arg[0]]=arg[1];
    }
  }

  //初始化方法
  start() {
    const appExample=this;
    this.oneEvent("onLaunch", function() {
      Reflect.set(this,"example",appExample);
      app=this;
    })
    //App方法调用的时候接受一个对象,会通过浅拷贝的方式将数据添加到app方法里
    App(this);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//Page.js
import Event from "./Event.js";
//获取全局的app对象
const app = getApp();

export default class PageModule extends Event {
  constructor(data) {
    super();
    //保存当前的pageModule
    const pageExample = this;
    this.oneEvent("onLoad", function() {
      Reflect.set(app, "page", {
        example: pageExample,
        page: this,
        route: this.route
      })
    });

    //判断是否传入data
    data&&this.extend(data);
  }
  
  //数据筛选方法
  static select(obj){
    const events={},
          data={};
    
    Object.keys(obj).forEach(key=>{

      if(typeof obj[key]==="function"){
        events[key]=obj[key];
      }else{
        data[key]=obj[key];
      }

    });

    return {events,data};
  }

  //导出事件实例
  extend(obj){
    const {events,data}=PageModule.select(obj);
    
    //添加事件
    for(let eType in events){
      this.addEvent(eType,obj[eType]);
    }
    //添加属性
    Object.assign(this,data);
  }

  //导出事件实例
  exports(...arg) {
    //需要导出的事件
    arg = arg.length ? arg : Object.keys(this.events);
    const events = {};
    arg.forEach(eType => {
      if (typeof this[eType] === "function") {
        events[eType] = this[eType];
      }else{
        throw new Error("没有"+eType+"事件");
      }
    });
    return events;
  }

  //初始化方法
  start(data) {
    //判断是否传入data
    data && this.extend(data);
    Page(this);
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

# 12.4 事件对象

和JS中的事件对象一样,也是传入event为事件对象

# 12.4.1 BaseEvent

BaseEvent 基础事件对象属性列表

属性 类型 说明
type String 事件类型
timeStamp Integer 事件生成时的时间戳(页面打开到触发事件所经过的毫秒数)
target Object 触发事件的组件的一些属性值集合
currentTarget Object 当前组件的一些属性值集合
  • target为触发事件的源组件

    属性 类型 说明
    id String 事件源组件的id
    tagName String 当前组件的类型
    dataset Object 事件源组件上由data-开头的自定义属性组成的集合(W3C规定自定义属性必须用data-开头定义)
  • currentTarget为事件绑定的当前组件

    属性 类型 说明
    id String 当前组件的id
    tagName String 当前组件的类型
    dataset Object 当前组件上由data-开头的自定义属性组成的集合

# 12.4.2 CustomEvent

CustomEvent 自定义事件对象属性列表(继承 BaseEvent)

属性 类型 说明
detail Object 额外的信息
  • detail

    自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息,点击事件的detail 带有的 x, y 同 pageX, pageY 代表距离文档左上角的距离

# 12.4.3 TouchEvent

TouchEvent 触摸事件对象属性列表(继承 BaseEvent)

属性 类型 说明
touches Array 触摸事件,当前停留在屏幕中的触摸点信息的数组
changedTouches Array 触摸事件,当前变化的触摸点信息的数组(同toucher对象,只不过是变化的数据才进入该数组)
  • touches

    touches 是一个数组,每个元素为一个 Touch 对象(canvas 触摸事件中携带的 touches 是 CanvasTouch 数组),表示当前停留在屏幕上的触摸点

# 12.4.3.1 Touch 对象
属性 类型 说明
identifier Number 触摸点的标识符
pageX, pageY Number 距离文档左上角的距离,文档的左上角为原点 ,横向为X轴,纵向为Y轴
clientX, clientY Number 距离页面可显示区域(屏幕除去导航条)左上角距离,横向为X轴,纵向为Y轴
# 12.4.3.2 CanvasTouch 对象
属性 类型 说明
identifier Number 触摸点的标识符
x, y Number 距离 Canvas 左上角的距离,Canvas 的左上角为原点 ,横向为X轴,纵向为Y轴

# 13.页面跳转

# 13.1 wx.switchTab

该方法用于跳转到 tabBar 页面(在下方的tabBar栏有的页面),并关闭其他所有非 tabBar 页面

该方法内部传入一个对象,对象有以下属性:

属性 类型 必填 说明
url string 需要跳转的 tabBar 页面的路径(需在 app.json 的 tabBar (opens new window) 字段定义的页面),路径后不能带参数。
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
//app.json
{
  "tabBar": {
    "list": [
      {
        "pagePath": "index",
        "text": "首页"
      },
      {
        "pagePath": "other",
        "text": "其他"
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//在页面的js文件中可以使用wx.switchTab()方法跳转到index页面
wx.switchTab({
  url: '/index'
})
1
2
3
4

# 13.2 wx.reLaunch

该方法跳转时会关闭所有页面,并且打开到应用内的某个页面

属性 类型 必填 说明
url string 需要跳转的应用内页面路径,路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2'
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
wx.reLaunch({
  url: 'test?id=1' //传入一个参数在新的页面可以在onload生命周期函数中获取
})
1
2
3
// test 
Page({ 
    onLoad (option) { 
        console.log(option.query);//[id:1] 
    } 
})
1
2
3
4
5
6

# 13.3 wx.redirectTo

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

属性 类型 必填 说明
url string 需要跳转的应用内非 tabBar 的页面的路径, 路径后可以带参数。参数与路径之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔;如 'path?key=value&key2=value2'
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
wx.redirectTo({
  url: 'test?id=1'
})
1
2
3

# 13.4 wx.navigateTo

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

**注意:**小程序中页面栈最多十层

属性 类型 必填 说明
url string 需要跳转的应用内非 tabBar 的页面的路径, 路径后可以带参数。参数与路径之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔;如 'path?key=value&key2=value2'
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
wx.navigateTo({
  url: 'test?id=1'
})
1
2
3
// test.js
Page({
  onLoad(option) {
    console.log(option.query)
  }
})
1
2
3
4
5
6

# 13.5 wx.navigateBack

该方法跳转时关闭当前页面,返回上一页面或多级页面

**注:**可通过 getCurrentPages()方法获取当前的页面栈,决定需要返回几层

属性 类型 必填 说明
delta number 返回的页面数,如果 delta 大于现有页面数,则返回到首页。
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
// 注意:调用 navigateTo 跳转时,调用该方法的页面会被加入堆栈,而 redirectTo 方法则不会。见下方示例代码

// 此处是A页面
wx.navigateTo({
  url: 'B?id=1'
})

// 此处是B页面
wx.navigateTo({
  url: 'C?id=1'
})

// 在C页面内 navigateBack,将返回A页面
wx.navigateBack({
  delta: 2
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 13.6 navigator标签

该标签专门用于制作页面跳转的链接,点击通过该标签包裹的内容可以跳转到指定的页面

属性 类型 默认值 必填 说明
target string self 在哪个目标上发生跳转,默认当前小程序
url string 当前小程序内的跳转链接
open-type string navigate 跳转方式
delta number 1 当 open-type 为 'navigateBack' 时有效,表示回退的层数
app-id string target="miniProgram"时有效,要打开的小程序 appId
path string target="miniProgram"时有效,打开的页面路径,如果为空则打开首页
extra-data object target="miniProgram"时有效,需要传递给目标小程序的数据,目标小程序可在 App.onLaunch()App.onShow() 中获取到这份数据。详情 (opens new window)
version string release target="miniProgram"时有效,要打开的小程序版本
hover-class string navigator-hover 指定点击时的样式类,当hover-class="none"时,没有点击态效果
hover-stop-propagation boolean false 指定是否阻止本节点的祖先节点出现点击态
hover-start-time number 50 按住后多久出现点击态,单位毫秒
hover-stay-time number 600 手指松开后点击态保留时间,单位毫秒
bindsuccess string target="miniProgram"时有效,跳转小程序成功
bindfail string target="miniProgram"时有效,跳转小程序失败
bindcomplete string target="miniProgram"时有效,跳转小程序完成
.navigator-hover {
  color: blue;
}
.other-navigator-hover {
  color: red;
}
1
2
3
4
5
6
<!-- sample.wxml -->
<view class="btn-area">
  <navigator
    url="/page/navigate/navigate?title=navigate"
    hover-class="navigator-hover"
  >
    跳转到新页面
  </navigator>
  <navigator
    url="../../redirect/redirect/redirect?title=redirect"
    open-type="redirect"
    hover-class="other-navigator-hover"
  >
    在当前页打开
  </navigator>
  <navigator
    url="/page/index/index"
    open-type="switchTab"
    hover-class="other-navigator-hover"
  >
    切换 Tab
  </navigator>
  <navigator
    target="miniProgram"
    open-type="navigate"
    app-id=""
    path=""
    extra-data=""
    version="release"
  >
    打开绑定的小程序
  </navigator>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- navigator.wxml -->
<view style="text-align:center">{{title}}</view>
<view>点击左上角返回回到之前页面</view>

<!-- redirect.wxml -->
<view style="text-align:center">{{title}}</view>
<view>点击左上角返回回到上级页面</view>
1
2
3
4
5
6
7
Page({
  onLoad(options) {
    this.setData({
      title: options.title
    })
  }
})
1
2
3
4
5
6
7

# 13.6.1 target

说明
self 当前小程序
miniProgram 其它小程序

**注意:**在跳转至其他小程序前,将统一增加弹窗,询问是否跳转,用户确认后才可以跳转其他小程序。如果用户点击取消,则回调 fail cancel

# 13.6.2 open-type

说明
navigate 对应 wx.navigateTowx.navigateToMiniProgram 的功能
redirect 对应 wx.redirectTo 的功能
switchTab 对应 wx.switchTab 的功能
reLaunch 对应 wx.reLaunch的功能
navigateBack 对应 wx.navigateBack 的功能
exit 退出小程序,target="miniProgram"时生效

# 13.6.3 version

说明
develop 开发版
trial 体验版
release 正式版,仅在当前小程序为开发版或体验版时此参数有效;如果当前小程序是正式版,则打开的小程序必定是正式版。

# 14.网络请求

# 14.1 发起请求

通过wx.request()方法能够发起HTTPS网络请求

属性 类型 默认值 必填 说明
url string 开发者服务器接口地址
data string/object/ArrayBuffer 请求的参数
header Object 设置请求的 header,header 中不能设置 Referer。 content-type 默认为 application/json
method string GET HTTP 请求方法
dataType string json 返回的数据格式
responseType string text 响应的数据类型
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
wx.request({
  url: 'test.php', // 仅为示例,并非真实的接口地址
  data: {
    x: '',
    y: ''
  },
  header: {
    'content-type': 'application/json' // 默认值
  },
  success(res) {
    console.log(res.data)
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13

# 14.2 下载数据

通过wx.downloadFile()方法将文件资源下载到本地,该方法由客户端直接发起一个 HTTPS GET 请求,返回文件的本地临时路径

**注意:**在服务端响应的 header 中指定合理的 Content-Type 字段,以保证客户端正确处理文件类型

属性 类型 必填 说明
url string 下载资源的 url
header Object HTTP 请求的 Header,Header 中不能设置 Referer
filePath string 指定文件下载后存储的路径
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

success时的回调函数:

属性(参数) 类型 说明
tempFilePath string 临时文件路径。如果没传入 filePath 指定文件存储路径,则下载后的文件会存储到一个临时文件
statusCode number 开发者服务器返回的 HTTP 状态码
wx.downloadFile({
  url: 'https://example.com/audio/123', // 仅为示例,并非真实的资源
  success(res) {
    // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容
    if (res.statusCode === 200) {
      wx.playVoice({
        filePath: res.tempFilePath
      })
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11

# 14.3 上传数据

通过 wx.uploadFile()函数将本地资源上传到服务器。客户端发起一个 HTTPS POST 请求,其中 content-typemultipart/form-data

属性 类型 必填 说明
url string 开发者服务器地址
filePath string 要上传文件资源的路径
name string 文件对应的 key,开发者在服务端可以通过这个 key 获取文件的二进制内容
header Object HTTP 请求 Header,Header 中不能设置 Referer
formData Object HTTP 请求中其他额外的 form data
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

object.success 回调函数:

属性(参数) 类型 说明
data string 开发者服务器返回的数据
statusCode number 开发者服务器返回的 HTTP 状态码
wx.chooseImage({
  success(res) {
    const tempFilePaths = res.tempFilePaths
    wx.uploadFile({
      url: 'https://example.weixin.qq.com/upload', // 仅为示例,非真实的接口地址
      filePath: tempFilePaths[0],
      name: 'file',
      formData: {
        user: 'test'
      },
      success(res) {
        const data = res.data
        // do something
      }
    })
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 15.数据缓存

# 15.1 异步

# 15.1.1 wx.setStorage

将数据存储在本地缓存中指定的 key 中,如果原来存在该key值则会覆盖掉原来该 key 对应的内容,数据存储生命周期跟小程序本身一致,即除用户主动删除或超过一定时间被自动清理,否则数据都一直可用

**注意:**单个 key 允许存储的最大数据长度为 1MB,所有数据存储上限为 10MB

属性 类型 必填 说明
key string 本地缓存中指定的 key
data any 需要存储的内容。只支持原生类型、Date、及能够通过JSON.stringify序列化的对象。
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
wx.setStorage({
    key: 'key',
    data: 'value',
    success(){

    },
    fail(){

    },
    complete(){
    }
})
1
2
3
4
5
6
7
8
9
10
11
12

# 15.1.2 wx.removeStorage

该方法用于从本地缓存中移除指定的key值

属性 类型 必填 说明
key string 本地缓存中指定的 key
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
wx.removeStorage({
    key: 'key',
    success(res) {
        console.log(res.data)
    },
    fail(){
 
    },
    complete(){
    }
})
1
2
3
4
5
6
7
8
9
10
11

# 15.1.3 wx.getStorage

该方法用于从本地缓存中异步获取指定 key 的内容

属性 类型 必填 说明
key string 本地缓存中指定的 key
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

success时的回调函数:

属性(参数) 类型 说明
data any key对应的内容
wx.getStorage({
  key: 'key',
  success(res) {
    console.log(res.data)
  }
})
1
2
3
4
5
6

# 15.1.4 wx.clearStorage

该方法用于清理所有的本地数据缓存

属性 类型 必填 说明
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)
wx.clearStorage()
1

# 15.1.5 wx.getStorageInfo

该方法用于异步获取当前storage的相关信息

属性 类型 必填 说明
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

success时的回调函数:

属性(属性) 类型 说明
keys Array<string> 当前 storage 中所有的 key
currentSize number 当前占用的空间大小, 单位 KB
limitSize number 限制的空间大小,单位 KB
wx.getStorageInfo({
  success(res) {
    console.log(res.keys)
    console.log(res.currentSize)
    console.log(res.limitSize)
  }
})
1
2
3
4
5
6
7

# 15.2 同步

# 15.2.1 wx.setStorageSync

该方法为同步设置缓存,只需要key值和对应的value作为字符串参数

try {
  wx.setStorageSync('key', 'value')
} catch (e) { }
1
2
3

# 15.2.2 wx.removeStorageSync

同步移除指定缓存,传入需要获取的键值作为参数

try {
  wx.removeStorageSync('key')
} catch (e) {
  // Do something when catch error
}
1
2
3
4
5

# 15.2.3 any wx.getStorageSync

同步获取指定缓存的值,传入需要获取的键值作为参数

try {
  const value = wx.getStorageSync('key')
  if (value) {
    // Do something with return value
  }
} catch (e) {
  // Do something when catch error
}
1
2
3
4
5
6
7
8

# 15.2.4 wx.clearStorageSync

同步清除所有的缓存数据

try {
  wx.clearStorageSync()
} catch (e) {
  // Do something when catch error
}
1
2
3
4
5

# 15.2.5 wx.getStorageInfoSync

同步获取到Storage中的信息,返回一个对象

属性 类型 说明
keys Array<string> 当前 storage 中所有的 key
currentSize number 当前占用的空间大小, 单位 KB
limitSize number 限制的空间大小,单位 KB
try {
  const res = wx.getStorageInfoSync()
  console.log(res.keys)
  console.log(res.currentSize)
  console.log(res.limitSize)
} catch (e) {
  // Do something when catch error
}
1
2
3
4
5
6
7
8

# 15.3 模拟数据库

//Stroage.js
//类的私有方法
const whereCompare = {
  //判定相等时
  "=": function(that, value) {
    return that === value;
  },
  //判定不相等时
  "!=": function(that, value) {
    return that !== value;
  },
  //判定大于时
  ">": function(that, value) {
    return that > value;
  },
  //判定小于时
  "<": function(that, value) {
    return that < value;
  },
  //判定大于等于时
  ">=": function(that, value) {
    return that >= value;
  },
  //判定小于等于时
  "<=": function(that, value) {
    return that <= value;
  },
  //模糊查找
  "like": function(that, value) {
    return new RegExp(value, 'i').test(that);
  }
}

//离线的类库
export default class Stroage {
  //构造函数
  constructor(dbname) {
    Object.assign(this, {
      dbname, //数据库名
      //类的缓存,存档和读档 
      cache: {
        add: {
          data: []
        }
      }
    });
  }
  //实时获取类中数据库的数据
  static getDb(dbname) {
    return wx.getStorageSync(dbname) || [];
  }
  //获取where的数据
  static getWhere(action) {
    if (this.whereFn) {
      const whereFn = this.whereFn;
      this.whereFn = null;
      return whereFn;
    } else {
      throw new Error("调用" + action + "方式时需要先调用where方法进行查询")
    }
  }

  //添加数据
  add(data) {
    //如果是一个数组(数组里成员也是对象),则循环递归为自己添加数据
    if (Array.isArray(data)) {
      data.forEach(item => {
        this.add(item);
      })
    } else if (typeof data === "object") {
      //如果是对象则push到add缓存中
      this.cache.add.data.push(data);
    } else {
      throw new Error("传入一个对象或以对象为成员的数组作为参数");
    }
    return this; //用作链式法则
  }
  //将缓存的数据更新到本地数据
  save() {
    //拿到本地数据
    let db = Stroage.getDb(this.dbname);

    //删除数据
    if (this.cache.del) {
      db = db.filter((item) => {
        return !this.cache.del.where(item);
      })
    }

    //更新数据
    if (this.cache.updated) {
      db.forEach(item => {
        if (this.cache.updated.where(item)) {
          Object.assign(item, this.cache.updated.data);
        }
      });
    }

    //判断缓存区是否原来有该缓存的数据
    if (this.cache.add) {
      db.push(...this.cache.add.data);
    }
    //更新本地缓存
    wx.setStorageSync(this.dbname, db);

    //更新数据缓存
    this.cache.add.data = [];
    return this;
  }

  //删除数据
  del() {
    this.cache.del = {
      where: Stroage.getWhere.call(this, "del")
    }
    return this;
  }

  //修改数据
  updated(data) {
    if (typeof data === "object") {
      //记录到实例缓存里面
      this.cache.updated = {
        data,
        where: Stroage.getWhere.call(this, "updated")
      }
    } else {
      throw new Error("updated只接受对象作为参数");
    }
    return this;
  }

  //搜索函数
  where(...args) {
    let [key, compare, value] = args;

    //判断是否为一个对象
    if (typeof key === "object") {

      for (let k in key) {
        if (Array.isArray(key[k])) {
          this.where(k, ...key[k]);
        } else {
          this.where(k, key[k]);
        }
      }
      return this;
    }


    if (value === undefined) {
      value = compare;
      compare = "=";
    }
    //获取对比函数
    const compareFn = whereCompare[compare]; //返会一个函数
    //是否为对应对比方式
    if (compareFn) {
      //构建where的查询函数
      if (!this.whereFn) {
        //构建一个where方法
        const whereFn = item => {

          //用来计数,统计成功的条件
          let compareNum = 0;
          whereFn.compare.forEach(compare => {
            compareNum += ~~compare.compareFn(item[compare.key], compare.value)
          });
          return compareNum === whereFn.compare.length;
        };
        //定义数组来存放的对比方式
        whereFn.compare = [];
        //赋值给this
        this.whereFn = whereFn;
      }
      //记录当前对比条件
      this.whereFn.compare.push({
        key,
        value,
        compareFn
      });
    } else {
      throw new Error("where不支持" + compare + "的对比方式");
    }
    return this;
  }

  //搜索时使用的查询函数,只查询一条数据
  find(data) {
    const db = Stroage.getDb(this.dbname);
    //如果需要排序
    this.sortFn && db.sort(this.sortFn);

    return db.find(Stroage.getWhere.call(this, "find"));
  }
  //查询多条数据
  select() {
    //先去本地拿数据,接着缓存合并保存
    const db = Stroage.getDb(this.dbname);
    const data = db.filter(Stroage.getWhere.call(this, "select"));
    //如果需要排序
    this.sortFn && data.sort(this.sortFn);
    //如果需要截取数据
    return this.sliceArg ? data.slice(...this.sliceArg) : data;
  }

  //直接拿到所以数据
  all(){
    const data = Stroage.getDb(this.dbname);
    //如果需要排序
    this.sortFn && data.sort(this.sortFn);
    //如果需要截取数据
    return this.sliceArg ? data.slice(...this.sliceArg) : data;
  }

  //排序方式
  order(key, sort = "asc") {
    this.sortFn = (a, b) => {
      return /desc/i.test(sort) ? b[key] - a[key] : a[key] - b[key];
    }
   
    return this;
  }

  //数据截取,从第几条开始取,取多少条
  limit(start, end) {
    if (end === undefined) { //只写一个参数默认从第一条开始取
      end = start;
      start = 0;
    } else {
      start--;
      end += start;
    }

    this.sliceArg = [start, end];

    return this;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// test.js
import Storage from 'Storage.js';
let db=new Storage("test");
db.add([{
  "a":1,
  "b":2
},{
  "a":2,
  "b":3
},{
    "a": 3,
    "b": 4
},{
    "a": 4,
    "b": 6
}]).save();


let data = db.where("a","<=", 3).order("a","desc").limit(1,2).updated({
  b:7
}).save().select();

console.log(data);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
最近更新时间: 2023/07/14 16:54:29
最近更新
01
2023/07/17 13:57:35
02
2023/07/17 10:12:59
03
2023/07/17 09:24:52