LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

微信小程序

1. 主体文件结构

主体部分由三个文件组成,必须放在项目的根目录,如下:

文件 必填 作用
app.js 小程序逻辑
app.json 小程序公共设置( 严格的JSON语法,不能用单引号,不能写注释,否则编译报错 )
app.wxss 小程序公共样式表( 拓展了rpx尺寸单位,微信专属响应式像素 )

2. 页面文件结构

页面由四个文件组成,分别是:

文件类型 必填 作用
js 页面逻辑 ( 微信小程序没有window和document对象 )
wxml 页面结构 ( XML语法,不是HTML语法 )
wxss 页面样式表 ( 拓展了rpx尺寸单位,微信专属响应式像素 )
json 页面配置 ( 严格的JSON语法,不能用单引号,不能写注释,否则编译报错 )

3. 其他文件

project.config.json : 项目的配置文件

sitemap.json : 用来配置小程序及其页面是否允许被微信索引(上线的时候使用)

{
  "rules":[{
    "action": "allow",
    "page": "path/to/page"
  }, {
    "action": "disallow",
    "page": "*"
  }]
}

小程序启动过程:

1.把小程序的代码包下载到本地

2.解析app.json全局配置文件

3.执行app.js小程序入口文件,调用App()创建小程序实例

4.渲染小程序首页

5.小程序启动完成

页面渲染过程

1.加载解析页面的.json配置文件

2.加载页面的.wxml模板和.wxss样式

3.执行页面的.js文件,调用Page()创建页面实例

4.页面渲染完成

2. 微信小程序-app.json

1. app.json 配置项列表

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html

{
  "pages": [
    "pages/index/index",
    "pages/logs/index"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  },
  "tabBar": {
    "color": "#dddddd",
    "selectedColor": "#3cc51f",
    "borderStyle": "black",
    "backgroundColor": "#ffffff",
    "list": [{
      "pagePath": "page/component/index",
      "iconPath": "image/icon_component.png",
      "selectedIconPath": "image/icon_component_HL.png",
      "text": "组件"
    }, {
      "pagePath": "page/API/index/index",
      "iconPath": "image/icon_API.png",
      "selectedIconPath": "image/icon_API_HL.png",
      "text": "接口"
    }]
  },
  "networkTimeout": {
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true,
  "navigateToMiniProgramAppIdList": [
    "wxe5f52902cf4de896"
  ]
}

//如果要新增页面可以直接在pages里面编辑

app.json文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。

属性 类型 必填 描述
pages String Array 设置页面路径
window Object 设置默认页面的窗口表现
tabBar Object 设置底部 tab 的表现
debug Boolean 设置是否开启 debug 模式
style 设置样式版本

2 .window配置

用于设置小程序的状态栏、导航条、标题、窗口背景色。

注意:页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现,所以无需写 window 这个键。

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如”#000000”
navigationBarTextStyle String white 导航栏标题颜色,仅支持 black/white
navigationBarTitleText String 导航栏标题文字内容
backgroundColor HexColor #ffffff 窗口的背景色,可以通过开启 “enablePullDownRefresh”:true来查看窗口颜色
backgroundTextStyle String dark 下拉刷新的背景字体、loading 图的样式,仅支持 dark/light
enablePullDownRefresh Boolean false 是否开启下拉刷新,详见页面相关事件处理函数
onReachBottomDistance Number 50 页面上拉触底事件触发时距页面底部距离,单位为px

3. tabBar

https://mp.weixin.qq.com/debug/wxadoc/dev/framework/config.html#tabbar

如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

Tip:

  1. 当设置 position 为 top 时,将不会显示 icon。
  2. tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 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 可选值 bottom、top,设置成top是无图标

其中 list 接受一个数组,数组中的每个项都是一个对象,其属性值如下:

属性 类型 必填 说明
pagePath String 页面路径,必须在 pages 中先定义
text String tab 上按钮文字
iconPath String 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效
selectedIconPath String 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效

4.页面配置–.json

在页面配置中,可以修改全局配置的部分信息,用来指定当前页面的一些特有配置

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如”#000000”
navigationBarTextStyle String white 导航栏标题颜色,仅支持 black/white
navigationBarTitleText String 导航栏标题文字内容
backgroundColor HexColor #ffffff 窗口的背景色,可以通过开启 “enablePullDownRefresh”:true来查看窗口颜色
backgroundTextStyle String dark 下拉刷新的背景字体、loading 图的样式,仅支持 dark/light
enablePullDownRefresh Boolean false 是否开启下拉刷新,详见页面相关事件处理函数
onReachBottomDistance Number 50 页面上拉触底事件触发时距页面底部距离,单位为px
disableScroll Boolean false 设置为true则页面整体不能上下滚动,只在页面配置中有效,无法在app.json中设置该项

3.wxml语法

wxml html
标签名称 view,text,image,navigator div,span,img,a
属性节点 <a href="#">超链接</a> <navigator url="/apges/home/home">超链接</navigator>

4.样式

与 CSS 相比,WXSS 扩展的特性有:

  • 尺寸单位(rpx rem)

  • vw和vh是css3中的新单位,是一种视窗单位,在小程序中同样适用

  • 样式导入

  • 提供了全局的样式和局部样式(项目根目录下的app.wxss会作用于所有小程序页面,局部页面的.wxss样式仅对当前页面生效)

  • wxss仅支持部分css选择器

    1.尺寸单位

    1.rpx(responsive pixel):

    ​ 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

    2.vw和vh:

    ​ 是css3中的新单位,是一种视窗单位,在小程序中也同样适用。

    小程序中,窗口宽度固定为100vw,将窗口宽度平均分成100份,1份是1vw

    小程序中,窗口高度固定为100vh ,将窗口高度平均分成100份,1份是1vh

    2.样式引入

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

    /** common.wxss **/
    .small-p {
      padding:5px;
    }
    
    /** app.wxss **/
    @import "common.wxss";
    .middle-p {
      padding:15px;
    }
    
      .box2{
          border: 1px solid red;
          width: 600rpx;
          height: 600rpx;
          /*background-image可以指定网络图片,但是不可以指定本地图片*/
          background-image: url("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586254613140&di=33474e16f9fc5342f7a1e2be72f287f6&imgtype=0&src=http%3A%2F%2Fdmimg.5054399.com%2Fallimg%2Fpkm%2Fpk%2F22.jpg");
          /* background-image: url(../../icons/timg.jpg); */
      }
    

3.选择器

特别需要注意的是小程序 不支持通配符 * ,因此以下代码无效!


  *{
    margin:0;
    padding:0;   
    box-sizing:border-box;
}

目前支持的选择器有:

选择器 样例 描述
.class .container 选择所有拥有class=”container”的组件
#id #main 选择拥有id=”main”的组件
element view 选择所有view组件
element,element view,text 选择所有文档的view组件和所有text组件
element element view text 选择view 下的所有text组件
::after view::after 在view组件后边插入内容
::before view::before 在view组件前边插入内容
nth-child(n) view:nth-child(n) 选择某个索引的标签

4.小程序中使用less

原声小程序不支持 less ,其他基于小程序的框架基本都支持,如 wepy , mpvue , taro ,uni-app等。
但是仅仅因为一个less功能,而去引用框架,肯定是不可取的。因此可以使用以下方式来实现

1. 编辑器是 vscode
2. 安装插件 easy less
3. 在vs code的设置中加如下,配置

{
    "workbench.sideBar.location": "left",
    "less.compile": {        
      "outExt":".wxss"
    }
}
  1. 在要编写样式的地方新建 less 文件,如 index.less ,然后正常编辑即可。

5.js文件分类

1.app.js:

是整个小程序项目的入口文件,通过调用App()函数来启动整个小程序

2.页面的.js文件:

是页面的入口文件,通过调用Page()函数来创建并运行页面

3.普通的.js文件:

普通的功能模块文件,用来封装公共的函数或属性供页面使用

4.小程序不是运行在浏览器中,所以没有window和document对象

1.小程序不是运行在浏览器中,所以没有window和document对象
console.log(window);
console.log(document);

2.小程序js的一些额外成员
App({})方法:用于创建应用程序实例,指定应用程序的生命周期
Page()方法:用于创建页面对象
getApp()方法:用于获取全局的应用程序对象
getCurrentPages()方法:用于获取当前页面的调用栈 
   # https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/route.html
wx : 微信小程序获取API的对象

5.import 和 include(小程序支持CommonJS规范)

1.小程序支持CommonJS规范
exports.aa 或者module.exports导出
require 引入

<import src="a.wxml"/>   作用可以引入目标文件中定义的template,然后在当前的wxml中使用template。注意点不能使用引入目标文件中所引入的第三方的template,比如A import B,B import C,A可以使用B中声明的template,但是A不可以使用C中声明的template

<include src="header.wxml"/>  作用可以引入目标文件中除了template wxs之外的所有代码

wxs语法

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

wxml中无法调用在页面的.js中定义的函数,但是可以调用wxs中定义的函数。WXS存在的意义就是充当computed/filter这类功能。经常配合Musttache语法进行使用,但是不能作为组件的事件回调函数

wxs有自己的数据类型,wxs不支持es6语法,并且遵循CommonJS规范

WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。

WXS 代码可以编写在 wxml 文件中的 <wxs> 标签内,或以 .wxs 为后缀名的文件内。

wxs的运行环境和其他JavaScript代码是隔离的,wxs中不能调用js中定义的函数,也不能调用小程序中提供的API

在IOS设备上,小程序内的WXS会比JavaScript代码快2-20倍,但在android设备上,两者运行效率无差异

https://developers.weixin.qq.com/miniprogram/dev/reference/wxs/

<!-- 声明一个wxs模块 : 不支持es6  需要指定模块的名字 module,方便在wxml中访问模块中的成员 -->
<wxs module="dateFormat">
  //准备一个时间格式化的方法
  var format = function(date){
      //根据时间戳使用小程序提供的getDate()方法得到一个时间
      var time = getDate(date);
      var year = time.getFullYear();
      var month = time.getMonth() + 1;
      var date = time.getDate();
      var hour = time.getHours();
      var minute = time.getMinutes();
      var second = time.getSeconds();
      month = month < 10 ? "0" + month : month;
      date = date < 10 ? "0" + date : date;
      hour = hour < 10 ? "0" + hour : hour;
      minute = minute < 10 ? "0" + minute : minute;
      second = second < 10 ? "0" + second : second;
      return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
  };

  var format2 = function(msg){
    var reg = getRegExp('小', "g");
    return msg.replace(reg, "大");
  }

  var msg = "hello world";

  //将外部需要使用的信息导出
  module.exports = {
    format : format,
    format2:format2
    msg:msg
  }
</wxs>
<text>{{dateFormat.format(now)}}</text>
<text>{{dateFormat.format2("小小的")}}</text>
<text>{{dateFormat.msg}}</text>

使用外联的wxs脚本

1.必须为<wxs>标签添加module和src属性

2.module用来指定模块的名称

3.src用来指定要引入的脚本的路径,且必须是相对路径

utils/tools.wxs
var toLower = function (str) {
  return str.toLowerCase();
};
module.exports = {
  toLower: toLower,
};


    index.wxml中
    <view>
        {{m2.toLower('HH')}}
    </view>
    <wxs module="m2" src="../../utils/index.wxs" />

5.wxml语法参考

1.数据绑定

WXML 中的动态数据均来自对应 Page 的 data。

简单绑定

数据绑定使用 Mustache 语法(双大括号)将变量包起来,可以作用于:

内容

<view> {{ message }} </view>
Page({
  data: {
    message: 'Hello MINA!'
  }
})

组件属性(需要在双引号之内)

<view src="{{imgurl}}"> </view>
Page({
  data: {
    imgurl: '/assets/imgs/icon-daokuanrenling.png'
  }
})

控制属性(需要在双引号之内)

<view wx:if="{{condition}}"> </view>
Page({
  data: {
    condition: true
  }
})

关键字(需要在双引号之内)

true:boolean 类型的 true,代表真值。

false: boolean 类型的 false,代表假值。

特别注意:不要直接写 checked=”false”,其计算结果是一个字符串,转成 boolean 类型后代表真值。

<checkbox checked="{{false}}"> </checkbox>

三元运算

<view hidden="{{flag ? true : false}}"> Hidden </view>

算数运算

<view> {{a + b}} + {{c}} + d </view>
Page({
  data: {
    a: 1,
    b: 2,
    c: 3
  }
})
view中的内容为 3 + 3 + d。

逻辑判断

<view wx:if="{{length > 5}}"> </view>

字符串运算

<view>{{"hello" + name}}</view>
Page({
  data:{
    name: 'MINA'
  }
})

数据路径运算

<view>{{object.key}} {{array[0]}}</view>
Page({
  data: {
    object: {
      key: 'Hello '
    },
    array: ['MINA']
  }
})

组合

也可以在 Mustache 内直接进行组合,构成新的对象或者数组。

数组

<view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view>
Page({
  data: {
    zero: 0
  }
})

最终组合成数组[0, 1, 2, 3, 4]

对象

<template is="objectCombine" data="{{for: a, bar: b}}"></template>
Page({
  data: {
    a: 1,
    b: 2
  }
})

最终组合成的对象是 {for: 1, bar: 2}

也可以用扩展运算符 ... 来将一个对象展开

<template is="objectCombine" data="{{...obj1, ...obj2, e: 5}}"></template>
Page({
  data: {
    obj1: {
      a: 1,
      b: 2
    },
    obj2: {
      c: 3,
      d: 4
    }
  }
})

最终组合成的对象是 {a: 1, b: 2, c: 3, d: 4, e: 5}

如果对象的 key 和 value 相同,也可以间接地表达。

<template is="objectCombine" data="{{foo, bar}}"></template>
Page({
  data: {
    foo: 'my-foo',
    bar: 'my-bar'
  }
})

最终组合成的对象是 {foo: 'my-foo', bar:'my-bar'}

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

<template is="objectCombine" data="{{...obj1, ...obj2, a, c: 6}}"></template>
Page({
  data: {
    obj1: {
      a: 1,
      b: 2
    },
    obj2: {
      b: 3,
      c: 4
    },
    a: 5
  }
})

最终组合成的对象是 {a: 5, b: 3, c: 6}

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

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

等同于

<view wx:for="{{[1,2,3] + ' '}}">
  {{item}}
</view>

列表渲染

wx:for

在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。

默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item

<view wx:for="{{array}}">
  {{index}}: {{item.message}}
</view>
Page({
  data: {
    array: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  }
})

使用 wx:for-item 可以指定数组当前元素的变量名,

使用 wx:for-index 可以指定数组当前下标的变量名:

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

wx:for 也可以嵌套,下边是一个九九乘法表

<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
  <view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
    <view wx:if="{{i <= j}}"> {{i}} * {{j}}="{{i" j}} < view> < code>

block wx:for

类似 block wx:if,也可以将 wx:for 用在<block/>标签上,以渲染一个包含多节点的结构块。例如:

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

wx:key

如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。(不需要用插值语法表示,直接使用即可)

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

  1. 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。

当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。

如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。

示例代码

在开发者工具中预览效果

<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>
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: function(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: function(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: function(e){
    this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray)
    this.setData({
      numberArray: this.data.numberArray
    })
  }
})

注意事项

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

<view wx:for="array">
  {{item}}
</view>

等同于

<view wx:for="{{['a','r','r','a','y']}}">
  {{item}}
</view>

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

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

等同于

<view wx:for="{{[1,2,3] + ' '}}" >
  {{item}}
</view>

条件渲染

wx:if

在框架中,使用 wx:if="" 来判断是否需要渲染该代码块:

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

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

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

block wx:if

因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/> 标签将多个组件包装起来,并在上边使用 wx:if 控制属性。

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

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

wx:if vs hidden

hidden: 为true时隐藏,为false时显示

因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。

同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。

相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。

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

模板

WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。

定义模板

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

<!--
  index: int
  msg: string
  time: string
-->
<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>

使用模板

使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:

<template is="msgItem" data="{{...item}}"/>
Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    }
  }
})

is 属性可以使用 Mustache 语法,来动态决定具体需要渲染哪个模板:

<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>

模板的作用域

模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的 <wxs /> 模块。

引用

WXML 提供两种文件引用方式importinclude

import

import可以在该文件中使用目标文件定义的template,如:

在 item.wxml 中定义了一个叫itemtemplate

<!-- item.wxml -->
<template name="item">
  <text>{{text}}</text>
</template>

在 index.wxml 中引用了 item.wxml,就可以使用item模板:

<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>

import 的作用域

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

如:C import B,B import A,在 C 中可以使用 B 定义的template,在 B 中可以使用 A 定义的template,但是 C 不能使用 A 定义的template

<!-- A.wxml -->
<template name="A">
  <text> A template </text>
</template>
<!-- B.wxml -->
<import src="a.wxml"/>
<template name="B">
  <text> B template </text>
</template>
<!-- C.wxml -->
<import src="b.wxml"/>
<template is="A"/>  <!-- Error! Can not use tempalte when not import A. -->
<template is="B"/>

include

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

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

事件

什么是事件

  • 事件是视图层到逻辑层的通讯方式。
  • 事件可以将用户的行为反馈到逻辑层进行处理。
  • 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
  • 事件对象可以携带额外信息,如 id, dataset, touches。

事件的使用方式

  • 在组件中绑定一个事件处理函数。

bindtap,当用户点击该组件的时候会在该页面对应的 Page 中找到相应的事件处理函数。

<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
  • 在相应的 Page 定义中写上相应的事件处理函数,参数是event。
Page({
  tapName: function(event) {
    console.log(event)
  }
})

事件分类

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

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

WXML的冒泡事件列表:

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

注:除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如 form 的submit事件,input 的input事件,scroll-view 的scroll事件,(详见各个组件)

类型 绑定方式 事件描述
tap bindtap或bind:tap
input bindinput或bind:input
change bindchange或bind:change

普通事件绑定

事件绑定的写法类似于组件的属性,如:

<view bindtap="handleTap">
    Click here!
</view>

如果用户点击这个 view ,则页面的 handleTap 会被调用。

事件绑定函数可以是一个数据绑定,如:

<view bindtap="{{ handlerName }}">
    Click here!
</view>

此时,页面的 this.data.handlerName 必须是一个字符串,指定事件处理函数名;如果它是个空字符串,则这个绑定会失效(可以利用这个特性来暂时禁用一些事件)。

自基础库版本 1.5.0 起,在大多数组件和自定义组件中, bind 后可以紧跟一个冒号,其含义不变,如 bind:tap 。基础库版本 2.8.1 起,在所有组件中开始提供这个支持。

绑定并阻止事件冒泡

bind 外,也可以用 catch 来绑定事件。与 bind 不同, catch 会阻止事件向上冒泡。

例如在下边这个例子中,点击 inner view 会先后调用handleTap3handleTap2(因为 tap 事件会冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父节点传递),点击 middle view 会触发handleTap2,点击 outer view 会触发handleTap1

<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view
    <view id="inner" bindtap="handleTap3">
      inner view
    </view>
  </view>
</view>

互斥事件绑定

自基础库版本 2.8.2 起,除 bindcatch 外,还可以使用 mut-bind 来绑定事件。一个 mut-bind 触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind 绑定函数不会被触发,但 bind 绑定函数和 catch 绑定函数依旧会被触发。

换而言之,所有 mut-bind 是“互斥”的,只会有其中一个绑定函数被触发。同时,它完全不影响 bindcatch 的绑定效果。

例如在下边这个例子中,点击 inner view 会先后调用 handleTap3handleTap2 ,点击 middle view 会调用 handleTap2handleTap1

<view id="outer" mut-bind:tap="handleTap1">
  outer view
  <view id="middle" bindtap="handleTap2">
    middle view
    <view id="inner" mut-bind:tap="handleTap3">
      inner view
    </view>
  </view>
</view>

事件的捕获阶段

自基础库版本 1.5.0 起,触摸类事件支持捕获阶段。捕获阶段位于冒泡阶段之前,且在捕获阶段中,事件到达节点的顺序与冒泡阶段恰好相反。需要在捕获阶段监听事件时,可以采用capture-bindcapture-catch关键字,后者将中断捕获阶段和取消冒泡阶段。

在下面的代码中,点击 inner view 会先后调用handleTap2handleTap4handleTap3handleTap1

<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>

如果将上面代码中的第一个capture-bind改为capture-catch,将只触发handleTap2

<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">
  outer view
  <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
    inner view
  </view>
</view>

事件对象

如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。

BaseEvent 基础事件对象属性列表:

属性 类型 说明 基础库版本
type String 事件类型
timeStamp Integer 事件生成时的时间戳
target Object 触发事件的组件的一些属性值集合
currentTarget Object 当前组件的一些属性值集合
mark Object 事件标记数据 2.7.1

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

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

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

属性 类型 说明
touches Array 触摸事件,当前停留在屏幕中的触摸点信息的数组
changedTouches Array 触摸事件,当前变化的触摸点信息的数组

特殊事件: canvas 中的触摸事件不可冒泡,所以没有 currentTarget。

type

代表事件的类型。

timeStamp

页面打开到触发事件所经过的毫秒数。

target

触发事件的源组件。

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

currentTarget

事件绑定的当前组件。

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

说明: target 和 currentTarget 可以参考上例中,点击 inner view 时,handleTap3 收到的事件对象 target 和 currentTarget 都是 inner,而 handleTap2 收到的事件对象 target 就是 inner,currentTarget 就是 middle。

dataset

在组件节点中可以附加一些自定义数据。这样,在事件中可以获取这些自定义的节点数据,用于事件的逻辑处理。

在 WXML 中,这些自定义数据以 data- 开头,多个单词由连字符 - 连接。这种写法中,连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符。如:

  • data-element-type ,最终会呈现为 event.currentTarget.dataset.elementType
  • data-elementType ,最终会呈现为 event.currentTarget.dataset.elementtype

示例:

<view data-alpha-beta="1" data-alphaBeta="2" bindtap="bindViewTap"> DataSet Test </view>
Page({
  bindViewTap:function(event){
    event.currentTarget.dataset.alphaBeta === 1 // - 会转为驼峰写法
    event.currentTarget.dataset.alphabeta === 2 // 大写会转为小写
  }
})

mark

在基础库版本 2.7.1 以上,可以使用 mark 来识别具体触发事件的 target 节点。此外, mark 还可以用于承载一些自定义数据(类似于 dataset )。

当事件触发时,事件冒泡路径上所有的 mark 会被合并,并返回给事件回调函数。(即使事件不是冒泡事件,也会 mark 。)

代码示例:

在开发者工具中预览效果

<view mark:myMark="last" bindtap="bindViewTap">
  <button mark:anotherMark="leaf" bindtap="bindButtonTap">按钮</button>
</view>

在上述 WXML 中,如果按钮被点击,将触发 bindViewTapbindButtonTap 两个事件,事件携带的 event.mark 将包含 myMarkanotherMark 两项。

Page({
  bindViewTap: function(e) {
    e.mark.myMark === "last" // true
    e.mark.anotherMark === "leaf" // true
  }
})

markdataset 很相似,主要区别在于: mark 会包含从触发事件的节点到根节点上所有的 mark: 属性值;而 dataset 仅包含一个节点的 data- 属性值。

细节注意事项:

  • 如果存在同名的 mark ,父节点的 mark 会被子节点覆盖。
  • 在自定义组件中接收事件时, mark 不包含自定义组件外的节点的 mark
  • 不同于 dataset ,节点的 mark 不会做连字符和大小写转换。

touches

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

Touch 对象

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

CanvasTouch 对象

属性 类型 说明 特殊说明
identifier Number 触摸点的标识符
x, y Number 距离 Canvas 左上角的距离,Canvas 的左上角为原点 ,横向为 X 轴,纵向为 Y 轴

changedTouches

changedTouches 数据格式同 touches。 表示有变化的触摸点,如从无变有(touchstart),位置变化(touchmove),从有变无(touchend、touchcancel)。

detail

自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息,详见组件定义中各个事件的定义。

点击事件的detail 带有的 x, y 同 pageX, pageY 代表距离文档左上角的距离。

事件绑定修改数据

 bindtap="handleclick"
 
 handleclick(){
      this.setData({
      count: this.data.count + 1
    })
 }

事件传参

小程序中的事件传参比较特殊,不能在绑定事件的同时为事件处理函数传递参数,因为小程序会把bindtap的属性值统一当作事件名称来处理。例如:下面代码将会被认为调用了一个名称为toLink(item.link)的函数,

 bindtap="toLink(item.link)"

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

    
bindtap="toLink"
data-link="{{item.link}}"

  toLink(e) {
    console.log("点击了图标,要跳转页面", e.currentTarget.dataset.link)
  },

bindinput的语法格式

实现文本框和data之间的数据同步

        <input
             placeholder="请输入"
             auto-focus
             bind:input="handleinput"
             value="{{msg}}"
    
        handleinput(e) {
            this.setData({
            msg: e.detail.value
            })
         },
    

网络数据请求

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

wx.request({
  url: 'example.php', //请求地址
  method:"get",//请求参数
  data: {  //请求参数
    x: '',
    y: ''
  },
  header: { //设置请求头
    'content-type': 'application/json' // 默认值
  },
 dataType:"json", //请求返回结果的数据类型
 success (res) {//请求成功的回调
    console.log(res.data)
  },   
fail: function(res) {},// 请求失败执行的回调函数
complete: function(res) {},// 接口调用结束的回调函数(调用成功、失败都会执行)
})

页面导航

1.声明式导航

  • 在页面上生命一个navigator导航组件

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

  • url表示要跳转的页面的地址,必须以/开头;open-type表示跳转的方式,如tabbar页面必须用switchTab,默认为navigate

  • open-type 说明 备注
    navigate 对应 wx.navigateTowx.navigateToMiniProgram 的功能 打开新页面,新页面会压栈
    redirect 对应 wx.redirectTo 的功能 页面重定向,新页面不会压栈,而是把原来的页面替换掉
    switchTab 对应 wx.switchTab 的功能 跳转tabBar页面,并关闭其他所有非 tabBar 页面
    reLaunch 对应 wx.reLaunch 的功能 重新加载指定页面,页面全部出栈,只留下新的页面
    navigateBack 对应 wx.navigateBackwx.navigateBackMiniProgram (基础库 2.24.4 版本支持)的功能 回到上一个页面,通过delta指定回退的层数,默认值为1,当delta超出历史记录,返回首页
    exit 退出小程序,target="miniProgram"时生效

    
       <!--hover-class="none" 跳转到Tabbar页面-->
        <navigator url="/pages/index/index" open-type="switchTab" >跳转index</navigator>
        <!--hover-class="none"后退导航-->
        <navigator url="/pages/index/index" open-type="navigateBack" delta="1">跳转index</navigator>
        <view>
        <!--hover-class="none" 导航传参-->
       <navigator url="/pages/demo2/demo2?name=zhangsan&age=12" hover-class="none">跳转demo2</navigator>
    </view>
    
    //2.demo2.js
    //在onload方法中通过options来获取页面1传递的参数
      onLoad: function (options) {
        console.log(options)
      },
    

2.编程式导航

wx.navigateTo({
  url: '/pagex/test/test?id=1',//导航传参
})
接收参数
onLoad(options){
    console.log(options)//options就是导航传递过来的参数
}

导航模式

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

<view>
   <navigator url="/pages/demo2/demo2?name=zhangsan&age=12" open-type="redirectTo" >跳转demo2</navigator>
</view>

navigate   打开新页面,新页面会压栈
redirect    页面重定向,新页面不会压栈,而是把原来的页面替换掉
navigateBack   回到上一个页面
reLaunch    重新加载指定页面,页面全部出栈,只留下新的页面
switchTab   跳转tabBar页面,并关闭其他所有非 tabBar 页面

//  https://segmentfault.com/a/1190000014996318?utm_source=index-hottest


//注意点:
navigate, redirect 只能打开非 tabBar 页面。
switchTab 只能打开 tabBar 页面。
reLaunch 可以打开任意页面。

//回退两层,可以通过getCurrentPages()方法来查看栈中的页面个数
wx.navigateBack({
    //当delta超出历史记录,返回首页
    delta:2
})

页面事件

1.下拉刷新:移动端中手指在屏幕下滑从而重新加载页面数据

全局开启下拉刷新:在app.json的window节点中,将enablePullDownRefresh设置为true

局部开启下拉刷新(推荐):在页面的json文件中将enablePullDownRefresh设置为true

{
  "usingComponents": {},
  "enablePullDownRefresh":true,//开启下拉刷新
  "backgroundColor":"#D8BFD8",//窗口的背景色
  "backgroundTextStyle":"light"//loading的样式颜色
}

在js中的onPullDownRefresh中可以监听到下拉刷新的操作,在这里进行数据的处理,但是下拉刷新后刷新的loading会一直显示,需要调用 wx.stopPullDownRefresh()停止下拉刷新的操作

2.上拉触底:移动端中手指在屏幕上滑从而重新加载更多数据,可以通过onReachBottomDistance来配置上拉触底触发事件的距离,默认为50px

在js中的onReachBottom中可以监听到上拉触底事件,这里应该进行节流的处理,上一个请求未处理完毕,不允许进行下一次请求

生命周期

应用生命周期:小程序从启动=>运行=>销毁的过程

页面生命周期:每个页面加载=>渲染=>销毁的过程

页面的生命周期范围较小,应用生命周期的范围较大,可以在特定的时间点执行某些特定的操作

应用生命周期


onLaunch:function(){}//小程序初始化完成,会触发onLaunch(全局只触发一次)
onShow:function(){}//小程序启动,或从后台进入前台显示,会触发onShow
onHide:function(){}//小程序从前台进入后台,会触发onHide

页面生命周期

onLoad:function(options){}//监听页面加载,一个页面只调用一次
onShow:function(){}//监听页面显示
onReady:function(){}//监听页面初次渲染完成,一个页面只调用一次
onHide:function(){}//监听页面隐藏
onUnLoad:function(){}//监听页面卸载,一个页面只调用一次
img_show