企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 小程序 视图 [TOC] # WXML WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合[基础组件](https://developers.weixin.qq.com/miniprogram/dev/component/index.html)、[事件系统](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html),可以构建出页面的结构。 用以下一些简单的例子来看看 WXML 具有什么能力: ### [数据绑定](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/data.html) ``` <!--wxml--> <view> {{message}} </view> ``` ``` // page.js Page({ data: { message: 'Hello MINA!' } }) ``` ### [列表渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/list.html) ``` <!--wxml--> <view wx:for="{{array}}"> {{item}} </view> ``` ``` // page.js Page({ data: { array: [1, 2, 3, 4, 5] } }) ``` ### [条件渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/conditional.html) ``` <!--wxml--> <view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view> <view wx:elif="{{view == 'APP'}}"> APP </view> <view wx:else="{{view == 'MINA'}}"> MINA </view> ``` ``` // page.js Page({ data: { view: 'MINA' } }) ``` ### [模板](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/template.html) ``` <!--wxml--> <template name="staffName"> <view> FirstName: {{firstName}}, LastName: {{lastName}} </view> </template> <template is="staffName" data="{{...staffA}}"></template> <template is="staffName" data="{{...staffB}}"></template> <template is="staffName" data="{{...staffC}}"></template> ``` ``` // page.js Page({ data: { staffA: {firstName: 'Hulk', lastName: 'Hu'}, staffB: {firstName: 'Shang', lastName: 'You'}, staffC: {firstName: 'Gideon', lastName: 'Lin'} } }) ``` ### [事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html) ``` <view bindtap="add"> {{count}} </view> ``` ``` Page({ data: { count: 1 }, add: function(e) { this.setData({ count: this.data.count + 1 }) } }) ``` 具体的能力以及使用方式在以下章节查看: [数据绑定](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/data.html)、[列表渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/list.html)、[条件渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/conditional.html)、[模板](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/template.html)、[事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html)、[引用](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/import.html) # 数据绑定 WXML 中的动态数据均来自对应 Page 的 data。 ## 简单绑定 数据绑定使用 Mustache 语法(双大括号)将变量包起来,可以作用于: ### 内容 ``` <view> {{ message }} </view> ``` ``` Page({ data: { message: 'Hello MINA!' } }) ``` ### 组件属性(需要在双引号之内) ``` <view id="item-{{id}}"> </view> ``` ``` Page({ data: { id: 0 } }) ``` ### 控制属性(需要在双引号之内) ``` <view wx:if="{{condition}}"> </view> ``` ``` Page({ data: { condition: true } }) ``` ### 关键字(需要在双引号之内) `true`:boolean 类型的 true,代表真值。 `false`: boolean 类型的 false,代表假值。 ``` <checkbox checked="{{false}}"> </checkbox> ``` *特别注意:不要直接写 `checked="false"`,其计算结果是一个字符串,转成 boolean 类型后代表真值。* ## 运算 可以在 `{{}}` 内进行简单的运算,支持的有如下几种方式: ### 三元运算 ``` <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> </view> </view> ``` ### 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`, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。 示例代码: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/tpg5tKmv6kZt "在开发者工具中预览效果") ``` <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="{{condition}}" 来判断是否需要渲染该代码块: <view wx:if="{{condition}}"> True </view> 也可以用 wx:elif 和 wx: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 因为 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 />` 模块。 # 事件 ## 什么是事件 * 事件是视图层到逻辑层的通讯方式。 * 事件可以将用户的行为反馈到逻辑层进行处理。 * 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。 * 事件对象可以携带额外信息,如 id, dataset, touches。 ## 事件的使用方式 * 在组件中绑定一个事件处理函数。 如`bindtap`,当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数。 ``` <view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! </view> ``` * 在相应的Page定义中写上相应的事件处理函数,参数是event。 ``` Page({ tapName: function(event) { console.log(event) } }) ``` * 可以看到log出来的信息大致如下: ``` { "type":"tap", "timeStamp":895, "target": { "id": "tapTest", "dataset": { "hi":"WeChat" } }, "currentTarget": { "id": "tapTest", "dataset": { "hi":"WeChat" } }, "detail": { "x":53, "y":14 }, "touches":[{ "identifier":0, "pageX":53, "pageY":14, "clientX":53, "clientY":14 }], "changedTouches":[{ "identifier":0, "pageX":53, "pageY":14, "clientX":53, "clientY":14 }] } ``` ## 事件详解 ### 事件分类 事件分为冒泡事件和非冒泡事件: 1. 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。 2. 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。 WXML的冒泡事件列表: | 类型 | 触发条件 | 最低版本 | | --- | --- | --- | | touchstart | 手指触摸动作开始 | | | touchmove | 手指触摸后移动 | | | touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | | | touchend | 手指触摸动作结束 | | | tap | 手指触摸后马上离开 | | | longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 | [1.5.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 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](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 1.9.90 开始支持,低版本需做兼容处理。") | 注:除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如[`<form/>`](https://developers.weixin.qq.com/miniprogram/dev/component/form.html)的`submit`事件,[`<input/>`](https://developers.weixin.qq.com/miniprogram/dev/component/input.html)的`input`事件,[`<scroll-view/>`](https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html)的`scroll`事件,(详见各个[组件](https://developers.weixin.qq.com/miniprogram/dev/component/index.html)) ### 事件绑定和冒泡 事件绑定的写法同组件的属性,以 key、value 的形式。 * key 以`bind`或`catch`开头,然后跟上事件的类型,如`bindtap`、`catchtouchstart`。自基础库版本 [1.5.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 1.5.0 开始支持,低版本需做兼容处理。") 起,在非[原生组件](https://developers.weixin.qq.com/miniprogram/dev/component/native-component.html)中,`bind`和`catch`后可以紧跟一个冒号,其含义不变,如`bind:tap`、`catch:touchstart`。 * value 是一个字符串,需要在对应的 Page 中定义同名的函数。不然当触发事件的时候会报错。 `bind`事件绑定不会阻止冒泡事件向上冒泡,`catch`事件绑定可以阻止冒泡事件向上冒泡。 如在下边这个例子中,点击 inner view 会先后调用`handleTap3`和`handleTap2`(因为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> ``` ### 事件的捕获阶段 自基础库版本 [1.5.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 1.5.0 开始支持,低版本需做兼容处理。") 起,触摸类事件支持捕获阶段。捕获阶段位于冒泡阶段之前,且在捕获阶段中,事件到达节点的顺序与冒泡阶段恰好相反。需要在捕获阶段监听事件时,可以采用`capture-bind`、`capture-catch`关键字,后者将中断捕获阶段和取消冒泡阶段。 在下面的代码中,点击 inner view 会先后调用`handleTap2`、`handleTap4`、`handleTap3`、`handleTap1`。 ``` <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](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#type) | String | 事件类型 | | [timeStamp](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#timestamp) | Integer | 事件生成时的时间戳 | | [target](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#target) | Object | 触发事件的组件的一些属性值集合 | | [currentTarget](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#currenttarget) | Object | 当前组件的一些属性值集合 | CustomEvent 自定义事件对象属性列表(继承 BaseEvent): | 属性 | 类型 | 说明 | | --- | --- | --- | | [detail](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#detail) | Object | 额外的信息 | TouchEvent 触摸事件对象属性列表(继承 BaseEvent): | 属性 | 类型 | 说明 | | --- | --- | --- | | [touches](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#touches) | Array | 触摸事件,当前停留在屏幕中的触摸点信息的数组 | | [changedTouches](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#changedtouches) | Array | 触摸事件,当前变化的触摸点信息的数组 | 特殊事件: `<canvas/>` 中的触摸事件不可冒泡,所以没有 currentTarget。 ### type 代表事件的类型。 ### timeStamp 页面打开到触发事件所经过的毫秒数。 ### target 触发事件的源组件。 | 属性 | 类型 | 说明 | | --- | --- | --- | | id | String | 事件源组件的id | | tagName | String | 当前组件的类型 | | [dataset](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#dataset) | Object | 事件源组件上由`data-`开头的自定义属性组成的集合 | ### currentTarget 事件绑定的当前组件。 | 属性 | 类型 | 说明 | | --- | --- | --- | | id | String | 当前组件的id | | tagName | String | 当前组件的类型 | | [dataset](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#dataset) | Object | 当前组件上由`data-`开头的自定义属性组成的集合 | 说明: target 和 currentTarget 可以参考上例中,点击 inner view 时,`handleTap3` 收到的事件对象 target 和 currentTarget 都是 inner,而 `handleTap2` 收到的事件对象 target 就是 inner,currentTarget 就是 middle。 #### dataset 在组件中可以定义数据,这些数据将会通过事件传递给 SERVICE。 书写方式: 以`data-`开头,多个单词由连字符`-`链接,不能有大写(大写会自动转成小写)如`data-element-type`,最终在 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 // 大写会转为小写 } }) ``` ### 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 自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息,详见[组件](https://developers.weixin.qq.com/miniprogram/dev/component)定义中各个事件的定义。 点击事件的`detail` 带有的 x, y 同 pageX, pageY 代表距离文档左上角的距离。 # 引用 WXML 提供两种文件引用方式`import`和`include`。 ### import `import`可以在该文件中使用目标文件定义的`template`,如: 在 item.wxml 中定义了一个叫`item`的`template`: ``` <!-- 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> ``` # WXSS WXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。 WXSS 用来决定 WXML 的组件应该怎么显示。 为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。 与 CSS 相比,WXSS 扩展的特性有: * 尺寸单位 * 样式导入 ### 尺寸单位 * rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。 | 设备 | rpx换算px (屏幕宽度/750) | px换算rpx (750/屏幕宽度) | | --- | --- | --- | | iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx | | iPhone6 | 1rpx = 0.5px | 1px = 2rpx | | iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx | 建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。 注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。 ### 样式导入 使用`@import`语句可以导入外联样式表,`@import`后跟需要导入的外联样式表的相对路径,用`;`表示语句结束。 示例代码: ``` /** common.wxss **/ .small-p { padding:5px; } ``` ``` /** app.wxss **/ @import "common.wxss"; .middle-p { padding:15px; } ``` ### 内联样式 框架组件上支持使用 style、class 属性来控制组件的样式。 * style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。 ``` <view style="color:{{color}};" /> ``` * class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上`.`,样式类名之间用空格分隔。 ``` <view class="normal_view" /> ``` ### 选择器 目前支持的选择器有: | 选择器 | 样例 | 样例描述 | | --- | --- | --- | | .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 组件前边插入内容 | ### 全局样式与局部样式 定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。 # 基础组件 框架为开发者提供了一系列基础组件,开发者可以通过组合这些基础组件进行快速开发。详细介绍请参考[组件文档](https://developers.weixin.qq.com/miniprogram/dev/component/index.html)。 什么是组件: * 组件是视图层的基本组成单元。 * 组件自带一些功能与微信风格一致的样式。 * 一个组件通常包括 `开始标签` 和 `结束标签`,`属性` 用来修饰这个组件,`内容` 在两个标签之内。 ``` <tagname property="value"> Content goes here ... </tagname> ``` 注意:所有组件与属性都是小写,以连字符`-`连接 ### 属性类型 | 类型 | 描述 | 注解 | | --- | --- | --- | | Boolean | 布尔值 | 组件写上该属性,不管是什么值都被当作 `true`;只有组件上没有该属性时,属性值才为`false`。 如果属性值为变量,变量的值会被转换为Boolean类型 | | Number | 数字 | `1`, `2.5` | | String | 字符串 | `"string"` | | Array | 数组 | `[ 1, "string" ]` | | Object | 对象 | `{ key: value }` | | EventHandler | 事件处理函数名 | `"handlerName"` 是 [Page](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/page.html) 中定义的事件处理函数名 | | Any | 任意属性 | | ### 公共属性 所有组件都有以下属性: | 属性名 | 类型 | 描述 | 注解 | | --- | --- | --- | --- | | id | String | 组件的唯一标示 | 保持整个页面唯一 | | class | String | 组件的样式类 | 在对应的 WXSS 中定义的样式类 | | style | String | 组件的内联样式 | 可以动态设置的内联样式 | | hidden | Boolean | 组件是否显示 | 所有组件默认显示 | | data-* | Any | 自定义属性 | 组件上触发的事件时,会发送给事件处理函数 | | bind* / catch* | EventHandler | 组件的事件 | 详见[事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html) | ### 特殊属性 几乎所有组件都有各自定义的属性,可以对该组件的功能或样式进行修饰,请参考各个[组件](https://developers.weixin.qq.com/miniprogram/dev/component/index.html)的定义。 # WXS WXS(WeiXin Script)是小程序的一套脚本语言,结合 `WXML`,可以构建出页面的结构。 ### 注意 1. wxs 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。 2. wxs 与 javascript 是不同的语言,有自己的语法,并不和 javascript 一致。 3. wxs 的运行环境和其他 javascript 代码是隔离的,wxs 中不能调用其他 javascript 文件中定义的函数,也不能调用小程序提供的API。 4. wxs 函数不能作为组件的事件回调。 5. 由于运行环境的差异,在 iOS 设备上小程序内的 wxs 会比 javascript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。 以下是一些使用 WXS 的简单示例: ### 页面渲染 ``` <!--wxml--> <wxs module="m1"> var msg = "hello world"; module.exports.message = msg; </wxs> <view> {{m1.message}} </view> ``` 页面输出: ``` hello world ``` ### 数据处理 ``` // page.js Page({ data: { array: [1, 2, 3, 4, 5, 1, 2, 3, 4] } }) ``` ``` <!--wxml--> <!-- 下面的 getMax 函数,接受一个数组,且返回数组中最大的元素的值 --> <wxs module="m1"> var getMax = function(array) { var max = undefined; for (var i = 0; i < array.length; ++i) { max = max === undefined ? array[i] : (max >= array[i] ? max : array[i]); } return max; } module.exports.getMax = getMax; </wxs> <!-- 调用 wxs 里面的 getMax 函数,参数为 page.js 里面的 array --> <view> {{m1.getMax(array)}} </view> ``` 页面输出: ``` 5 ``` # WXS 模块 WXS 代码可以编写在 wxml 文件中的 `<wxs>` 标签内,或以 `.wxs` 为后缀名的文件内。 ## 模块 每一个 `.wxs` 文件和 `<wxs>` 标签都是一个单独的模块。 每个模块都有自己独立的作用域。即在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见。 一个模块要想对外暴露其内部的私有变量与函数,只能通过 `module.exports` 实现。 ## .wxs 文件 在微信开发者工具里面,右键可以直接创建 `.wxs` 文件,在其中直接编写 WXS 脚本。 示例代码: ``` // /pages/comm.wxs var foo = "'hello world' from comm.wxs"; var bar = function(d) { return d; } module.exports = { foo: foo, bar: bar }; ``` 上述例子在 `/pages/comm.wxs` 的文件里面编写了 WXS 代码。该 `.wxs` 文件可以被其他的 `.wxs` 文件 或 WXML 中的 `<wxs>` 标签引用。 ### module 对象 每个 `wxs` 模块均有一个内置的 `module` 对象。 #### 属性 * `exports`: 通过该属性,可以对外共享本模块的私有变量与函数。 示例代码: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/KYgu1Km36pZP "在开发者工具中预览效果") ``` // /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"; ``` ``` <!-- 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 ``` ### require函数 在`.wxs`模块中引用其他 `wxs` 文件模块,可以使用 `require` 函数。 引用的时候,要注意如下几点: * 只能引用 `.wxs` 文件模块,且必须使用相对路径。 * `wxs` 模块均为单例,`wxs` 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 `wxs` 模块对象。 * 如果一个 `wxs` 模块在定义之后,一直没有被引用,则该模块不会被解析与运行。 示例代码: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/E4g94Kme6rZ6 "在开发者工具中预览效果") ``` // /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"; ``` ``` // /pages/logic.wxs var tools = require("./tools.wxs"); console.log(tools.FOO); console.log(tools.bar("logic.wxs")); console.log(tools.msg); ``` ``` <!-- /page/index/index.wxml --> <wxs src="./../logic.wxs" module="logic" /> ``` 控制台输出: ``` 'hello world' from tools.wxs logic.wxs some msg ``` ## `<wxs>` 标签 | 属性名 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | module | String | | 当前 `<wxs>` 标签的模块名。必填字段。 | | src | String | | 引用 .wxs 文件的相对路径。仅当本标签为单闭合标签或标签的内容为空时有效。 | ### module 属性 module 属性是当前 `<wxs>` 标签的模块名。在单个 wxml 文件内,建议其值唯一。有重复模块名则按照先后顺序覆盖(后者覆盖前者)。不同文件之间的 wxs 模块名不会相互覆盖。 module 属性值的命名必须符合下面两个规则: * 首字符必须是:字母(a-zA-Z),下划线(_) * 剩余字符可以是:字母(a-zA-Z),下划线(_), 数字(0-9) 示例代码: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/IzgmKKmn6tZ6 "在开发者工具中预览效果") ``` <!--wxml--> <wxs module="foo"> var some_msg = "hello world"; module.exports = { msg : some_msg, } </wxs> <view> {{foo.msg}} </view> ``` 页面输出: ``` hello world ``` 上面例子声明了一个名字为 `foo` 的模块,将 `some_msg` 变量暴露出来,供当前页面使用。 ### src 属性 src 属性可以用来引用其他的 `wxs` 文件模块。 引用的时候,要注意如下几点: * 只能引用 `.wxs` 文件模块,且必须使用相对路径。 * `wxs` 模块均为单例,`wxs` 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 `wxs` 模块对象。 * 如果一个 `wxs` 模块在定义之后,一直没有被引用,则该模块不会被解析与运行。 示例代码: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/C7gYFKmh6vZI "在开发者工具中预览效果") ``` // /pages/index/index.js Page({ data: { msg: "'hello wrold' from js", } }) ``` ``` <!-- /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 ``` 上述例子在文件 `/page/index/index.wxml` 中通过 `<wxs>` 标签引用了 `/page/comm.wxs` 模块。 ### 注意 * `<wxs>` 模块只能在定义模块的 WXML 文件中被访问到。使用 `<include>` 或 `<import>` 时,`<wxs>` 模块不会被引入到对应的 WXML 文件中。 * `<template>` 标签中,只能使用定义该 `<template>` 的 WXML 文件中定义的 `<wxs>` 模块。 # 变量 ## 概念 * WXS 中的变量均为值的引用。 * 没有声明的变量直接赋值使用,会被定义为全局变量。 * 如果只声明变量而不赋值,则默认值为 `undefined`。 * var表现与javascript一致,会有变量提升。 ``` var foo = 1; var bar = "hello world"; var i; // i === undefined ``` 上面代码,分别声明了 `foo`、 `bar`、 `i` 三个变量。然后,`foo` 赋值为数值 `1` ,`bar` 赋值为字符串 `"hello world"`。 ## 变量名 变量命名必须符合下面两个规则: * 首字符必须是:字母(a-zA-Z),下划线(_) * 剩余字符可以是:字母(a-zA-Z),下划线(_), 数字(0-9) ## 保留标识符 以下标识符不能作为变量名: ``` delete void typeof null undefined NaN Infinity var if else true false require this function arguments return for while do break continue switch case default ``` # 注释 WXS 主要有 3 种注释的方法。 示例代码: ``` <!-- wxml --> <wxs module="sample"> // 方法一:单行注释 /* 方法二:多行注释 */ /* 方法三:结尾注释。即从 /* 开始往后的所有 WXS 代码均被注释 var a = 1; var b = 2; var c = "fake"; </wxs> ``` 上述例子中,所有 WXS 代码均被注释掉了。 > 方法三 和 方法二 的唯一区别是,没有 `*/` 结束符。 > # 运算符 ## 基本运算符 示例代码: ``` var a = 10, b = 20; // 加法运算 console.log(30 === a + b); // 减法运算 console.log(-10 === a - b); // 乘法运算 console.log(200 === a * b); // 除法运算 console.log(0.5 === a / b); // 取余运算 console.log(10 === a % b); ``` * 加法运算(`+`)也可以用作字符串的拼接。 ``` var a = '.w' , b = 'xs'; // 字符串拼接 console.log('.wxs' === a + b); ``` ## 一元运算符 示例代码: ``` var a = 10, b = 20; // 自增运算 console.log(10 === a++); console.log(12 === ++a); // 自减运算 console.log(12 === a--); console.log(10 === --a); // 正值运算 console.log(10 === +a); // 负值运算 console.log(0-10 === -a); // 否运算 console.log(-11 === ~a); // 取反运算 console.log(false === !a); // delete 运算 console.log(true === delete a.fake); // void 运算 console.log(undefined === void a); // typeof 运算 console.log("number" === typeof a); ``` ## 位运算符 示例代码: ``` var a = 10, b = 20; // 左移运算 console.log(80 === (a << 3)); // 无符号右移运算 console.log(2 === (a >> 2)); // 带符号右移运算 console.log(2 === (a >>> 2)); // 与运算 console.log(2 === (a & 3)); // 异或运算 console.log(9 === (a ^ 3)); // 或运算 console.log(11 === (a | 3)); ``` ## 比较运算符 示例代码: ``` var a = 10, b = 20; // 小于 console.log(true === (a < b)); // 大于 console.log(false === (a > b)); // 小于等于 console.log(true === (a <= b)); // 大于等于 console.log(false === (a >= b)); ``` ## 等值运算符 示例代码: ``` var a = 10, b = 20; // 等号 console.log(false === (a == b)); // 非等号 console.log(true === (a != b)); // 全等号 console.log(false === (a === b)); // 非全等号 console.log(true === (a !== b)); ``` ## 赋值运算符 示例代码: ``` var a = 10; a = 10; a *= 10; console.log(100 === a); a = 10; a /= 5; console.log(2 === a); a = 10; a %= 7; console.log(3 === a); a = 10; a += 5; console.log(15 === a); a = 10; a -= 11; console.log(-1 === a); a = 10; a <<= 10; console.log(10240 === a); a = 10; a >>= 2; console.log(2 === a); a = 10; a >>>= 2; console.log(2 === a); a = 10; a &= 3; console.log(2 === a); a = 10; a ^= 3; console.log(9 === a); a = 10; a |= 3; console.log(11 === a); ``` ## 二元逻辑运算符 示例代码: ``` var a = 10, b = 20; // 逻辑与 console.log(20 === (a && b)); // 逻辑或 console.log(10 === (a || b)); ``` ## 其他运算符 示例代码: ``` var a = 10, b = 20; //条件运算符 console.log(20 === (a >= 10 ? a + 10 : b + 10)); //逗号运算符 console.log(20 === (a, b)); ``` ## 运算符优先级 | 优先级 | 运算符 | 说明 | 结合性 | | --- | --- | --- | --- | | 20 | `(` ... `)` | 括号 | n/a | | 19 | ... `.` ... | 成员访问 | 从左到右 | | | ... `[` ... `]` | 成员访问 | 从左到右 | | | ... `(` ... `)` | 函数调用 | 从左到右 | | 17 | ... `++` | 后置递增 | n/a | | | ... `--` | 后置递减 | n/a | | 16 | `!` ... | 逻辑非 | 从右到左 | | | `~` ... | 按位非 | 从右到左 | | | `+` ... | 一元加法 | 从右到左 | | | `-` ... | 一元减法 | 从右到左 | | | `++` ... | 前置递增 | 从右到左 | | | `--` ... | 前置递减 | 从右到左 | | | `typeof` ... | typeof | 从右到左 | | | `void` ... | void | 从右到左 | | | `delete` ... | delete | 从右到左 | | 14 | ... `*` ... | 乘法 | 从左到右 | | | ... `/` ... | 除法 | 从左到右 | | | ... `%` ... | 取模 | 从左到右 | | 13 | ... `+` ... | 加法 | 从左到右 | | | ... `-` ... | 减法 | 从左到右 | | 12 | ... `<<` ... | 按位左移 | 从左到右 | | | ... `>>` ... | 按位右移 | 从左到右 | | | ... `>>>` ... | 无符号右移 | 从左到右 | | 11 | ... `<` ... | 小于 | 从左到右 | | | ... `<=` ... | 小于等于 | 从左到右 | | | ... `>` ... | 大于 | 从左到右 | | | ... `>=` ... | 大于等于 | 从左到右 | | 10 | ... `==` ... | 等号 | 从左到右 | | | ... `!=` ... | 非等号 | 从左到右 | | | ... `===` ... | 全等号 | 从左到右 | | | ... `!==` ... | 非全等号 | 从左到右 | | 9 | ... `&` ... | 按位与 | 从左到右 | | 8 | ... `^` ... | 按位异或 | 从左到右 | | 7 | ... `|` ... | 按位或 | 从左到右 | | 6 | ... `&&` ... | 逻辑与 | 从左到右 | | 5 | ... `||` ... | 逻辑或 | 从左到右 | | 4 | ... `?` ... `:` ... | 条件运算符 | 从右到左 | | 3 | ... `=` ... | 赋值 | 从右到左 | | | ... `+=` ... | 赋值 | 从右到左 | | | ... `-=` ... | 赋值 | 从右到左 | | | ... `*=` ... | 赋值 | 从右到左 | | | ... `/=` ... | 赋值 | 从右到左 | | | ... `%=` ... | 赋值 | 从右到左 | | | ... `<<=` ... | 赋值 | 从右到左 | | | ... `>>=` ... | 赋值 | 从右到左 | | | ... `>>>=` ... | 赋值 | 从右到左 | | | ... `&=` ... | 赋值 | 从右到左 | | | ... `^=` ... | 赋值 | 从右到左 | | | ... `|=` ... | 赋值 | 从右到左 | | 0 | ... `,` ... | 逗号 | 从左到右 |# 语句 ## if 语句 在 WXS 中,可以使用以下格式的 `if` 语句 : * `if (expression) statement` : 当 `expression` 为 truthy 时,执行 `statement`。 * `if (expression) statement1 else statement2` : 当 `expression` 为 truthy 时,执行 `statement1`。 否则,执行 `statement2` * `if ... else if ... else statementN` 通过该句型,可以在 `statement1` ~ `statementN` 之间选其中一个执行。 示例语法: ``` // if ... if (表达式) 语句; if (表达式) 语句; if (表达式) { 代码块; } // if ... else if (表达式) 语句; else 语句; if (表达式) 语句; else 语句; if (表达式) { 代码块; } else { 代码块; } // if ... else if ... else ... if (表达式) { 代码块; } else if (表达式) { 代码块; } else if (表达式) { 代码块; } else { 代码块; } ``` ## switch 语句 示例语法: ``` switch (表达式) { case 变量: 语句; case 数字: 语句; break; case 字符串: 语句; default: 语句; } ``` * `default` 分支可以省略不写。 * `case` 关键词后面只能使用:`变量`,`数字`,`字符串`。 示例代码: ``` var exp = 10; switch ( exp ) { case "10": console.log("string 10"); break; case 10: console.log("number 10"); break; case exp: console.log("var exp"); break; default: console.log("default"); } ``` 输出: ``` number 10 ``` ## for 语句 示例语法: ``` for (语句; 语句; 语句) 语句; for (语句; 语句; 语句) { 代码块; } ``` * 支持使用 `break`,`continue` 关键词。 示例代码: ``` for (var i = 0; i < 3; ++i) { console.log(i); if( i >= 1) break; } ``` 输出: ``` 0 1 ``` ## while 语句 示例语法: ``` while (表达式) 语句; while (表达式){ 代码块; } do { 代码块; } while (表达式) ``` * 当`表达式`为 true 时,循环执行`语句`或`代码块`。 * 支持使用 `break`,`continue` 关键词。 # 数据类型 WXS 语言目前共有以下几种数据类型: * `number` : 数值 * `string` :字符串 * `boolean`:布尔值 * `object`:对象 * `function`:函数 * `array` : 数组 * `date`:日期 * `regexp`:正则 ## number ### 语法 number 包括两种数值:整数,小数。 ``` var a = 10; var PI = 3.141592653589793; ``` ### 属性 * `constructor`:返回字符串 `"Number"`。 ### 方法 * `toString` * `toLocaleString` * `valueOf` * `toFixed` * `toExponential` * `toPrecision` > 以上方法的具体使用请参考 `ES5` 标准。 ## string ### 语法 string 有两种写法: ``` 'hello world'; "hello world"; ``` ### 属性 * `constructor`:返回字符串 `"String"`。 * `length` > 除constructor外属性的具体含义请参考 `ES5` 标准。 ### 方法 * `toString` * `valueOf` * `charAt` * `charCodeAt` * `concat` * `indexOf` * `lastIndexOf` * `localeCompare` * `match` * `replace` * `search` * `slice` * `split` * `substring` * `toLowerCase` * `toLocaleLowerCase` * `toUpperCase` * `toLocaleUpperCase` * `trim` > 以上方法的具体使用请参考 `ES5` 标准。 ## boolean ### 语法 布尔值只有两个特定的值:`true` 和 `false`。 ### 属性 * `constructor`:返回字符串 `"Boolean"`。 ### 方法 * `toString` * `valueOf` > 以上方法的具体使用请参考 `ES5` 标准。 ## object ### 语法 object 是一种无序的键值对。使用方法如下所示: ``` var o = {} //生成一个新的空对象 //生成一个新的非空对象 o = { 'string' : 1, //object 的 key 可以是字符串 const_var : 2, //object 的 key 也可以是符合变量定义规则的标识符 func : {}, //object 的 value 可以是任何类型 }; //对象属性的读操作 console.log(1 === o['string']); console.log(2 === o.const_var); //对象属性的写操作 o['string']++; o['string'] += 10; o.const_var++; o.const_var += 10; //对象属性的读操作 console.log(12 === o['string']); console.log(13 === o.const_var); ``` ### 属性 * `constructor`:返回字符串 `"Object"`。 ``` console.log("Object" === {k:"1",v:"2"}.constructor) ``` ### 方法 * `toString`:返回字符串 `"[object Object]"`。 ## function ### 语法 function 支持以下的定义方式: ``` //方法 1 function a (x) { return x; } //方法 2 var b = function (x) { return x; } ``` function 同时也支持以下的语法(匿名函数,闭包等): ``` var a = function (x) { return function () { return x;} } var b = a(100); console.log( 100 === b() ); ``` ### arguments function 里面可以使用 `arguments` 关键词。该关键词目前只支持以下的属性: * `length`: 传递给函数的参数个数。 * `[index]`: 通过 `index` 下标可以遍历传递给函数的每个参数。 示例代码: ``` var a = function(){ console.log(3 === arguments.length); console.log(100 === arguments[0]); console.log(200 === arguments[1]); console.log(300 === arguments[2]); }; a(100,200,300); ``` ### 属性 * `constructor`:返回字符串 `"Function"`。 * `length`:返回函数的形参个数。 ### 方法 * `toString`:返回字符串 `"[function Function]"`。 示例代码: ``` var func = function (a,b,c) { } console.log("Function" === func.constructor); console.log(3 === func.length); console.log("[function Function]" === func.toString()); ``` ## array ### 语法 array 支持以下的定义方式: ``` var a = []; //生成一个新的空数组 a = [1,"2",{},function(){}]; //生成一个新的非空数组,数组元素可以是任何类型 ``` ### 属性 * `constructor`:返回字符串 `"Array"`。 * `length` > 除constructor外属性的具体含义请参考 `ES5` 标准。 ### 方法 * `toString` * `concat` * `join` * `pop` * `push` * `reverse` * `shift` * `slice` * `sort` * `splice` * `unshift` * `indexOf` * `lastIndexOf` * `every` * `some` * `forEach` * `map` * `filter` * `reduce` * `reduceRight` > 以上方法的具体使用请参考 `ES5` 标准。 ## date ### 语法 生成 date 对象需要使用 `getDate`函数, 返回一个当前时间的对象。 ``` getDate() getDate(milliseconds) getDate(datestring) getDate(year, month[, date[, hours[, minutes[, seconds[, milliseconds]]]]]) ``` * 参数 * `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 (中国标准时间) ``` ### 属性 * `constructor`:返回字符串 “Date”。 ### 方法 * `toString` * `toDateString` * `toTimeString` * `toLocaleString` * `toLocaleDateString` * `toLocaleTimeString` * `valueOf` * `getTime` * `getFullYear` * `getUTCFullYear` * `getMonth` * `getUTCMonth` * `getDate` * `getUTCDate` * `getDay` * `getUTCDay` * `getHours` * `getUTCHours` * `getMinutes` * `getUTCMinutes` * `getSeconds` * `getUTCSeconds` * `getMilliseconds` * `getUTCMilliseconds` * `getTimezoneOffset` * `setTime` * `setMilliseconds` * `setUTCMilliseconds` * `setSeconds` * `setUTCSeconds` * `setMinutes` * `setUTCMinutes` * `setHours` * `setUTCHours` * `setDate` * `setUTCDate` * `setMonth` * `setUTCMonth` * `setFullYear` * `setUTCFullYear` * `toUTCString` * `toISOString` * `toJSON` > 以上方法的具体使用请参考 `ES5` 标准。 ## regexp ### 语法 生成 regexp 对象需要使用 `getRegExp`函数。 ``` getRegExp(pattern[, flags]) ``` * 参数: * `pattern`: 正则表达式的内容。 * `flags`:修饰符。该字段只能包含以下字符: * `g`: global * `i`: ignoreCase * `m`: multiline。 示例代码: ``` 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); ``` ### 属性 * `constructor`:返回字符串 `"RegExp"`。 * `source` * `global` * `ignoreCase` * `multiline` * `lastIndex` > 除constructor外属性的具体含义请参考 `ES5` 标准。 ### 方法 * `exec` * `test` * `toString` > 以上方法的具体使用请参考 `ES5` 标准。 ## 数据类型判断 ### `constructor` 属性 数据类型的判断可以使用 `constructor` 属性。 示例代码: ``` var number = 10; console.log( "Number" === number.constructor ); var string = "str"; console.log( "String" === string.constructor ); var boolean = true; console.log( "Boolean" === boolean.constructor ); var object = {}; console.log( "Object" === object.constructor ); var func = function(){}; console.log( "Function" === func.constructor ); var array = []; console.log( "Array" === array.constructor ); var date = getDate(); console.log( "Date" === date.constructor ); var regexp = getRegExp(); console.log( "RegExp" === regexp.constructor ); ``` ### `typeof` 使用 `typeof` 也可以区分部分数据类型。 示例代码: ``` var number = 10; var boolean = true; var object = {}; var func = function(){}; var array = []; var date = getDate(); var regexp = getRegExp(); console.log( 'number' === typeof number ); console.log( 'boolean' === typeof boolean ); console.log( 'object' === typeof object ); console.log( 'function' === typeof func ); console.log( 'object' === typeof array ); console.log( 'object' === typeof date ); console.log( 'object' === typeof regexp ); console.log( 'undefined' === typeof undefined ); console.log( 'object' === typeof null ); ``` # 基础类库 ## console `console.log` 方法用于在 console 窗口输出信息。它可以接受多个参数,将它们的结果连接起来输出。 ## Math ### 属性 * `E` * `LN10` * `LN2` * `LOG2E` * `LOG10E` * `PI` * `SQRT1_2` * `SQRT2` > 以上属性的具体使用请参考 `ES5` 标准。 ### 方法 * `abs` * `acos` * `asin` * `atan` * `atan2` * `ceil` * `cos` * `exp` * `floor` * `log` * `max` * `min` * `pow` * `random` * `round` * `sin` * `sqrt` * `tan` > 以上方法的具体使用请参考 `ES5` 标准。 ## JSON ### 方法 * `stringify(object)`: 将 `object` 对象转换为 `JSON` 字符串,并返回该字符串。 * `parse(string)`: 将 `JSON` 字符串转化成对象,并返回该对象。 示例代码: ``` console.log(undefined === JSON.stringify()); console.log(undefined === JSON.stringify(undefined)); console.log("null"===JSON.stringify(null)); console.log("111"===JSON.stringify(111)); console.log('"111"'===JSON.stringify("111")); console.log("true"===JSON.stringify(true)); console.log(undefined===JSON.stringify(function(){})); console.log(undefined===JSON.parse(JSON.stringify())); console.log(undefined===JSON.parse(JSON.stringify(undefined))); console.log(null===JSON.parse(JSON.stringify(null))); console.log(111===JSON.parse(JSON.stringify(111))); console.log("111"===JSON.parse(JSON.stringify("111"))); console.log(true===JSON.parse(JSON.stringify(true))); console.log(undefined===JSON.parse(JSON.stringify(function(){}))); ``` ## Number ### 属性 * `MAX_VALUE` * `MIN_VALUE` * `NEGATIVE_INFINITY` * `POSITIVE_INFINITY` > 以上属性的具体使用请参考 `ES5` 标准。 ## Date ### 属性 * `parse` * `UTC` * `now` > 以上属性的具体使用请参考 `ES5` 标准。 ## Global ### 属性 * `NaN` * `Infinity` * `undefined` > 以上属性的具体使用请参考 `ES5` 标准。 ### 方法 * `parseInt` * `parseFloat` * `isNaN` * `isFinite` * `decodeURI` * `decodeURIComponent` * `encodeURI` * `encodeURIComponent` > 以上方法的具体使用请参考 `ES5` 标准。 > # 获取界面上的节点信息 ## WXML节点信息 [节点信息查询 API](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createSelectorQuery.html) 可以用于获取节点属性、样式、在界面上的位置等信息。 最常见的用法是使用这个接口来查询某个节点的当前位置,以及界面的滚动位置。 示例代码: ``` const query = wx.createSelectorQuery() query.select('#the-id').boundingClientRect(function(res){ res.top // #the-id 节点的上边界坐标(相对于显示区域) }) query.selectViewport().scrollOffset(function(res){ res.scrollTop // 显示区域的竖直滚动位置 }) query.exec() ``` 上述示例中, `#the-id` 是一个节点选择器,与 CSS 的选择器相近但略有区别,请参见 [SelectorQuery.select](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.select.html) 的相关说明。 在自定义组件中,推荐使用 `this.createSelectorQuery` 来代替 `wx.createSelectorQuery` ,这样会将选择器选取范围定在这个自定义组件内。 ## WXML节点布局相交状态 [节点布局相交状态 API](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createIntersectionObserver.html) 可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。 这一组API涉及的主要概念如下。 * 参照节点:监听的参照节点,取它的布局区域作为参照区域。如果有多个参照节点,则会取它们布局区域的 交集 作为参照区域。页面显示区域也可作为参照区域之一。 * 目标节点:监听的目标,默认只能是一个节点(使用 `selectAll` 选项时,可以同时监听多个节点)。 * 相交区域:目标节点的布局区域与参照区域的相交区域。 * 相交比例:相交区域占参照区域的比例。 * 阈值:相交比例如果达到阈值,则会触发监听器的回调函数。阈值可以有多个。 以下示例代码可以在目标节点(用选择器 `.target-class` 指定)每次进入或离开页面显示区域时,触发回调函数。 示例代码: ``` Page({ onLoad: function(){ wx.createIntersectionObserver().relativeToViewport().observe('.target-class', (res) => { res.id // 目标节点 id res.dataset // 目标节点 dataset res.intersectionRatio // 相交区域占目标节点的布局区域的比例 res.intersectionRect // 相交区域 res.intersectionRect.left // 相交区域的左边界坐标 res.intersectionRect.top // 相交区域的上边界坐标 res.intersectionRect.width // 相交区域的宽度 res.intersectionRect.height // 相交区域的高度 }) } }) ``` 以下示例代码可以在目标节点(用选择器 `.target-class` 指定)与参照节点(用选择器 `.relative-class` 指定)在页面显示区域内相交或相离,且相交或相离程度达到目标节点布局区域的20%和50%时,触发回调函数。 示例代码: ``` Page({ onLoad: function(){ wx.createIntersectionObserver(this, { thresholds: [0.2, 0.5] }).relativeTo('.relative-class').relativeToViewport().observe('.target-class', (res) => { res.intersectionRatio // 相交区域占目标节点的布局区域的比例 res.intersectionRect // 相交区域 res.intersectionRect.left // 相交区域的左边界坐标 res.intersectionRect.top // 相交区域的上边界坐标 res.intersectionRect.width // 相交区域的宽度 res.intersectionRect.height // 相交区域的高度 }) } }) ``` 在自定义组件中,推荐使用 `this.createIntersectionObserver` 来代替 `wx.createIntersectionObserver` ,这样会将选择器选取范围定在这个自定义组件内。 # 响应显示区域变化 ## 显示区域尺寸 显示区域指小程序界面中可以自由布局展示的区域。在默认情况下,小程序显示区域的尺寸自页面初始化起就不会发生变化。但以下两种方式都可以改变这一默认行为。 ### 在手机上启用屏幕旋转支持 从小程序基础库版本 [2.4.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.4.0 开始支持,低版本需做兼容处理。") 开始,小程序在手机上支持屏幕旋转。使小程序中的页面支持屏幕旋转的方法是:在 `app.json` 的 `window` 段中设置 `"pageOrientation": "auto"` ,或在页面 json 文件中配置 `"pageOrientation": "auto"` 。 以下是在单个页面 json 文件中启用屏幕旋转的示例。 代码示例: ``` { "pageOrientation": "auto" } ``` 如果页面添加了上述声明,则在屏幕旋转时,这个页面将随之旋转,显示区域尺寸也会随着屏幕旋转而变化。 ### 在 iPad 上启用屏幕旋转支持 从小程序基础库版本 [2.3.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.3.0 开始支持,低版本需做兼容处理。") 开始,在 iPad 上运行的小程序可以支持屏幕旋转。使小程序支持 iPad 屏幕旋转的方法是:在 `app.json` 中添加 `"resizable": true` 。 代码示例: ``` { "resizable": true } ``` 如果小程序添加了上述声明,则在屏幕旋转时,小程序将随之旋转,显示区域尺寸也会随着屏幕旋转而变化。注意:在 iPad 上不能单独配置某个页面是否支持屏幕旋转。 ## Media Query 有时,对于不同尺寸的显示区域,页面的布局会有所差异。此时可以使用 media query 来解决大多数问题。 代码示例: ``` .my-class { width: 40px; } @media (min-width: 480px) { /* 仅在 480px 或更宽的屏幕上生效的样式规则 */ .my-class { width: 200px; } } ``` ## 屏幕旋转事件 有时,仅仅使用 media query 无法控制一些精细的布局变化。此时可以使用 js 作为辅助。 在 js 中读取页面的显示区域尺寸,可以使用 [selectorQuery.selectViewport](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.selectViewport.html) 。 页面尺寸发生改变的事件,可以使用 `wx.onWindowResize` 页面的 `onResize` 来监听。对于自定义组件,可以使用 resize 生命周期来监听。回调函数中将返回显示区域的尺寸信息。 代码示例: ``` wx.onWindowResize(function(res) { res.size.windowWidth // 新的显示区域宽度 res.size.windowHeight // 新的显示区域高度 }) ``` ``` Page({ onResize(res) { res.size.windowWidth // 新的显示区域宽度 res.size.windowHeight // 新的显示区域高度 } }) ``` ``` Component({ pageLifetimes: { resize(res) { res.size.windowWidth // 新的显示区域宽度 res.size.windowHeight // 新的显示区域高度 } } }) ``` Bug & tips: * Bug: Android 微信版本 6.7.3 中, `live-pusher` 组件在屏幕旋转时方向异常。 # 组件模板和样式 类似于页面,自定义组件拥有自己的 `wxml` 模板和 `wxss` 样式。 ### 组件模板 组件模板的写法与页面模板相同。组件模板与组件数据结合后生成的节点树,将被插入到组件的引用位置上。 在组件模板中可以提供一个 `<slot>` 节点,用于承载组件引用时提供的子节点。 代码示例: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/1udXLnmi6KY2 "在开发者工具中预览效果") ``` <!-- 组件模板 --> <view class="wrapper"> <view>这里是组件的内部节点</view> <slot></slot> </view> ``` ``` <!-- 引用组件的页面模板 --> <view> <component-tag-name> <!-- 这部分内容将被放置在组件 <slot> 的位置上 --> <view>这里是插入到组件slot中的内容</view> </component-tag-name> </view> ``` 注意,在模板中引用到的自定义组件及其对应的节点名需要在 `json` 文件中显式定义,否则会被当作一个无意义的节点。除此以外,节点名也可以被声明为[抽象节点](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/generics.html)。 ### 模板数据绑定 与普通的 WXML 模板类似,可以使用数据绑定,这样就可以向子组件的属性传递动态数据。 代码示例: ``` <!-- 引用组件的页面模板 --> <view> <component-tag-name prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}"> <!-- 这部分内容将被放置在组件 <slot> 的位置上 --> <view>这里是插入到组件slot中的内容</view> </component-tag-name> </view> ``` 在以上例子中,组件的属性 `propA` 和 `propB` 将收到页面传递的数据。页面可以通过 `setData` 来改变绑定的数据字段。 注意:这样的数据绑定只能传递 JSON 兼容数据。自基础库版本 [2.0.9](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.0.9 开始支持,低版本需做兼容处理。") 开始,还可以在数据中包含函数(但这些函数不能在 WXML 中直接调用,只能传递给子组件)。 ### 组件wxml的slot 在组件的wxml中可以包含 `slot` 节点,用于承载组件使用者提供的wxml结构。 默认情况下,一个组件的wxml中只能有一个slot。需要使用多slot时,可以在组件js中声明启用。 ``` Component({ options: { multipleSlots: true // 在组件定义时的选项中启用多slot支持 }, properties: { /* ... */ }, methods: { /* ... */ } }) ``` 此时,可以在这个组件的wxml中使用多个slot,以不同的 `name` 来区分。 ``` <!-- 组件模板 --> <view class="wrapper"> <slot name="before"></slot> <view>这里是组件的内部细节</view> <slot name="after"></slot> </view> ``` 使用时,用 `slot` 属性来将节点插入到不同的slot上。 ``` <!-- 引用组件的页面模板 --> <view> <component-tag-name> <!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 --> <view slot="before">这里是插入到组件slot name="before"中的内容</view> <!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 --> <view slot="after">这里是插入到组件slot name="after"中的内容</view> </component-tag-name> </view> ``` ### 组件样式 组件对应 `wxss` 文件的样式,只对组件wxml内的节点生效。编写组件样式时,需要注意以下几点: * 组件和引用组件的页面不能使用id选择器(`#a`)、属性选择器(`[a]`)和标签名选择器,请改用class选择器。 * 组件和引用组件的页面中使用后代选择器(`.a .b`)在一些极端情况下会有非预期的表现,如遇,请避免使用。 * 子元素选择器(`.a>.b`)只能用于 `view` 组件与其子节点之间,用于其他组件可能导致非预期的情况。 * 继承样式,如 `font` 、 `color` ,会从组件外继承到组件内。 * 除继承样式外, `app.wxss` 中的样式、组件所在页面的的样式对自定义组件无效。 ``` #a { } /* 在组件中不能使用 */ [a] { } /* 在组件中不能使用 */ button { } /* 在组件中不能使用 */ .a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */ ``` 除此以外,组件可以指定它所在节点的默认样式,使用 `:host` 选择器(需要包含基础库 [1.7.2](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 1.7.2 开始支持,低版本需做兼容处理。") 或更高版本的开发者工具支持)。 代码示例: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/jAgvwKm16bZD "在开发者工具中预览效果") ``` /* 组件 custom-component.wxss */ :host { color: yellow; } ``` ``` <!-- 页面的 WXML --> <custom-component>这段文本是黄色的</custom-component> ``` ### 外部样式类 有时,组件希望接受外部传入的样式类(类似于 `view` 组件的 `hover-class` 属性)。此时可以在 `Component` 中用 `externalClasses` 定义段定义若干个外部样式类。这个特性从小程序基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 1.9.90 开始支持,低版本需做兼容处理。") 开始支持。 注意:在同一个节点上使用普通样式类和外部样式类时,两个类的优先级是未定义的,因此最好避免这种情况。 代码示例: ``` /* 组件 custom-component.js */ Component({ externalClasses: ['my-class'] }) ``` ``` <!-- 组件 custom-component.wxml --> <custom-component class="my-class">这段文本的颜色由组件外的 class 决定</custom-component> ``` 这样,组件的使用者可以指定这个样式类对应的 class ,就像使用普通属性一样。 代码示例: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/rbgNNKmE6bZK "在开发者工具中预览效果") ``` <!-- 页面的 WXML --> <custom-component my-class="red-text" /> ``` ``` .red-text { color: red; } ``` ### 全局样式类 使用外部样式类可以让组件使用指定的组件外样式类,如果希望组件外样式类能够完全影响组件内部,可以将组件构造器中的`options.addGlobalClass`字段置为`true`。这个特性从小程序基础库版本 [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.2.3 开始支持,低版本需做兼容处理。") 开始支持。 > 当开放了全局样式类,存在外部样式污染组件样式的风险,请谨慎选择。 代码示例: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/yNPLQvm87b1F "在开发者工具中预览效果") ``` /* 组件 custom-component.js */ Component({ options: { addGlobalClass: true, } }) ``` ``` <!-- 组件 custom-component.wxml --> <text class="red-text">这段文本的颜色由组件外的 class 决定</text> ``` ``` /* 组件外的样式定义 */ .red-text { color: red; } ``` # Component构造器 ## 定义段与示例方法 Component构造器可用于定义组件,调用Component构造器时可以指定组件的属性、数据、方法等。 | 定义段 | 类型 | 是否必填 | 描述 | 最低版本 | | --- | --- | --- | --- | --- | | properties | Object Map | 否 | 组件的对外属性,是属性名到属性设置的映射表,属性设置中可包含三个字段, `type` 表示属性类型、 `value` 表示属性初始值、 `observer` 表示属性值被更改时的响应函数 | | | data | Object | 否 | 组件的内部数据,和 `properties` 一同用于组件的模板渲染 | | | methods | Object | 否 | 组件的方法,包括事件响应函数和任意的自定义方法,关于事件响应函数的使用,参见 [组件事件](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html) | | | behaviors | String Array | 否 | 类似于mixins和traits的组件间代码复用机制,参见 [behaviors](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html) | | | created | Function | 否 | 组件生命周期函数,在组件实例进入页面节点树时执行,注意此时不能调用 `setData` | | | attached | Function | 否 | 组件生命周期函数,在组件实例进入页面节点树时执行 | | | ready | Function | 否 | 组件生命周期函数,在组件布局完成后执行,此时可以获取节点信息(使用 [SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html) ) | | | moved | Function | 否 | 组件生命周期函数,在组件实例被移动到节点树另一个位置时执行 | | | detached | Function | 否 | 组件生命周期函数,在组件实例被从页面节点树移除时执行 | | | relations | Object | 否 | 组件间关系定义,参见 [组件间关系](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/relations.html) | | | externalClasses | String Array | 否 | 组件接受的外部样式类,参见 [外部样式类](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html) | | | options | Object Map | 否 | 一些选项(文档中介绍相关特性时会涉及具体的选项设置,这里暂不列举) | | | lifetimes | Object | 否 | 组件生命周期声明对象,组件的生命周期:`created`、`attached`、`ready`、`moved`、`detached`将收归到`lifetimes`字段内进行声明,原有声明方式仍旧有效,如同时存在两种声明方式,则`lifetimes`字段内声明方式优先级最高 | [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.2.3 开始支持,低版本需做兼容处理。") | | pageLifetimes | Object | 否 | 组件所在页面的生命周期声明对象,支持页面的 `show` 、 `hide` 、 `resize` 生命周期 | [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.2.3 开始支持,低版本需做兼容处理。") ; `resize`为 [2.4.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.4.0 开始支持,低版本需做兼容处理。") | | definitionFilter | Function | 否 | 定义段过滤器,用于自定义组件扩展,参见 [自定义组件扩展](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/extend.html) | [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.2.3 开始支持,低版本需做兼容处理。") | 生成的组件实例可以在组件的方法、生命周期函数和属性 `observer` 中通过 `this` 访问。组件包含一些通用属性和方法。 | 属性名 | 类型 | 描述 | | --- | --- | --- | | is | String | 组件的文件路径 | | id | String | 节点id | | dataset | String | 节点dataset | | data | Object | 组件数据,包括内部数据和属性值 | | properties | Object | 组件数据,包括内部数据和属性值(与 `data` 一致) | | 方法名 | 参数 | 描述 | | --- | --- | --- | | setData | Object `newData` | 设置data并执行视图层渲染 | | hasBehavior | Object `behavior` | 检查组件是否具有 `behavior` (检查时会递归检查被直接或间接引入的所有behavior) | | triggerEvent | String `name`, Object `detail`, Object `options` | 触发事件,参见 [组件事件](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html) | | createSelectorQuery | | 创建一个 [SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html) 对象,选择器选取范围为这个组件实例内 | | createIntersectionObserver | | 创建一个 [IntersectionObserver](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html) 对象,选择器选取范围为这个组件实例内 | | selectComponent | String `selector` | 使用选择器选择组件实例节点,返回匹配到的第一个组件实例对象(会被 `wx://component-export` 影响) | | selectAllComponents | String `selector` | 使用选择器选择组件实例节点,返回匹配到的全部组件实例对象组成的数组 | | getRelationNodes | String `relationKey` | 获取这个关系所对应的所有关联节点,参见 [组件间关系](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/relations.html) | | groupSetData | Function `callback` | 立刻执行 `callback` ,其中的多个 `setData` 之间不会触发界面绘制(只有某些特殊场景中需要,如用于在不同组件同时 setData 时进行界面绘制同步) | 代码示例: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/GqVTnvmq7g1m "在开发者工具中预览效果") ``` Component({ behaviors: [], properties: { myProperty: { // 属性名 type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型) value: '', // 属性初始值(可选),如果未指定则会根据类型选择一个 observer: function(newVal, oldVal, changedPath) { // 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange' // 通常 newVal 就是新设置的数据, oldVal 是旧数据 } }, myProperty2: String // 简化的定义方式 }, data: {}, // 私有数据,可用于模板渲染 lifetimes: { // 生命周期函数,可以为函数,或一个在methods段中定义的方法名 attached: function () { }, moved: function () { }, detached: function () { }, }, // 生命周期函数,可以为函数,或一个在methods段中定义的方法名 attached: function () { }, // 此处attached的声明会被lifetimes字段中的声明覆盖 ready: function() { }, pageLifetimes: { // 组件所在页面的生命周期函数 show: function () { }, hide: function () { }, resize: function () { }, }, methods: { onMyButtonTap: function(){ this.setData({ // 更新属性和数据的方法与更新页面数据的方法类似 }) }, // 内部方法建议以下划线开头 _myPrivateMethod: function(){ // 这里将 data.A[0].B 设为 'myPrivateData' this.setData({ 'A[0].B': 'myPrivateData' }) }, _propertyChange: function(newVal, oldVal) { } } }) ``` 注意:在 `properties` 定义段中,属性名采用驼峰写法(`propertyName`);在 `wxml` 中,指定属性值时则对应使用连字符写法(`component-tag-name property-name="attr value"`),应用于数据绑定时采用驼峰写法(`attr="{{propertyName}}"`)。 ## 使用 Component 构造器构造页面 事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用 `Component` 构造器构造,拥有与普通组件一样的定义段与实例方法。但此时要求对应 json 文件中包含 `usingComponents` 定义段。 此时,组件的属性可以用于接收页面的参数,如访问页面 `/pages/index/index?paramA=123&paramB=xyz` ,如果声明有属性 `paramA` 或 `paramB` ,则它们会被赋值为 `123` 或 `xyz` 。 页面的生命周期方法(即 `on` 开头的方法),应写在 `methods` 定义段中。 代码示例: ``` { "usingComponents": {} } ``` ``` Component({ properties: { paramA: Number, paramB: String, }, methods: { onLoad: function() { this.data.paramA // 页面参数 paramA 的值 this.data.paramB // 页面参数 paramB 的值 } } }) ``` #### Bug & Tips: * 使用 `this.data` 可以获取内部数据和属性值,但不要直接修改它们,应使用 `setData` 修改。 * 生命周期函数无法在组件方法中通过 `this` 访问到。 * 属性名应避免以 data 开头,即不要命名成 `dataXyz` 这样的形式,因为在 WXML 中, `data-xyz=""` 会被作为节点 dataset 来处理,而不是组件属性。 * 在一个组件的定义和使用时,组件的属性名和 data 字段相互间都不能冲突(尽管它们位于不同的定义段中)。 * 从基础库 [2.0.9](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.0.9 开始支持,低版本需做兼容处理。") 开始,对象类型的属性和 data 字段中可以包含函数类型的子字段,即可以通过对象类型的属性字段来传递函数。低于这一版本的基础库不支持这一特性。 * `bug` : 对于 type 为 Object 或 Array 的属性,如果通过该组件自身的 this.setData 来改变属性值的一个子字段,则依旧会触发属性 observer ,且 observer 接收到的 `newVal` 是变化的那个子字段的值, `oldVal` 为空, `changedPath` 包含子字段的字段名相关信息。 # 组件间通信与事件 ## 组件间通信 组件间的基本通信方式有以下几种。 * WXML 数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据(自基础库版本 [2.0.9](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基础库 2.0.9 开始支持,低版本需做兼容处理。") 开始,还可以在数据中包含函数)。具体在 [组件模板和样式](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html) 章节中介绍。 * 事件:用于子组件向父组件传递数据,可以传递任意数据。 * 如果以上两种方式不足以满足需要,父组件还可以通过 `this.selectComponent` 方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。 ## 监听事件 事件系统是组件间通信的主要方式之一。自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件。关于事件的基本概念和用法,参见 [事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html)。 监听自定义组件事件的方法与监听基础组件事件的方法完全一致: 代码示例: ``` <!-- 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法 --> <component-tag-name bindmyevent="onMyEvent" /> <!-- 或者可以写成 --> <component-tag-name bind:myevent="onMyEvent" /> ``` ``` Page({ onMyEvent: function(e){ e.detail // 自定义组件触发事件时提供的detail对象 } }) ``` ## 触发事件 自定义组件触发事件时,需要使用 `triggerEvent` 方法,指定事件名、detail对象和事件选项: 代码示例: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/DFfYSKmI6vZD "在开发者工具中预览效果") ``` <!-- 在自定义组件中 --> <button bindtap="onTap">点击这个按钮将触发“myevent”事件</button> ``` ``` Component({ properties: {} methods: { onTap: function(){ var myEventDetail = {} // detail对象,提供给事件监听函数 var myEventOption = {} // 触发事件的选项 this.triggerEvent('myevent', myEventDetail, myEventOption) } } }) ``` 触发事件的选项包括: | 选项名 | 类型 | 是否必填 | 默认值 | 描述 | | --- | --- | --- | --- | --- | | bubbles | Boolean | 否 | false | 事件是否冒泡 | | composed | Boolean | 否 | false | 事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部 | | capturePhase | Boolean | 否 | false | 事件是否拥有捕获阶段 | 关于冒泡和捕获阶段的概念,请阅读 [事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html) 章节中的相关说明。 代码示例: [在开发者工具中预览效果](https://developers.weixin.qq.com/s/UGfljKm66zZ1 "在开发者工具中预览效果") ``` // 页面 page.wxml <another-component bindcustomevent="pageEventListener1"> <my-component bindcustomevent="pageEventListener2"></my-component> </another-component> ``` ``` // 组件 another-component.wxml <view bindcustomevent="anotherEventListener"> <slot /> </view> ``` ``` // 组件 my-component.wxml <view bindcustomevent="myEventListener"> <slot /> </view> ``` ``` // 组件 my-component.js Component({ methods: { onTap: function(){ this.triggerEvent('customevent', {}) // 只会触发 pageEventListener2 this.triggerEvent('customevent', {}, { bubbles: true }) // 会依次触发 pageEventListener2 、 pageEventListener1 this.triggerEvent('customevent', {}, { bubbles: true, composed: true }) // 会依次触发 pageEventListener2 、 anotherEventListener 、 pageEventListener1 } } }) ```