小程序相关整理
小程序简介
小程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。小程序开发主要面对的是两大操作系统ios和android的微信客户端; MVVM
是关键;
小程序代码构成
###文件支持
支持wxml,wxss,js,json,wxs
以及图片文件的预览;
###JSON 配置文件
在小程序中JSON是用于静态配置;
app.json
和project.config.json
和pages/logs
目录下还有一个logs.json
;
-
小程序配置 app.json
- 小程序的全局配置,包括所有页面路径,界面表现,网络超时时间,底部tab;
- pages 字段: 描述当前小程序所有页面路径,这是为了让wx客户端知道当前你的小程序页面定义在那个目录;
- window 字段: 定义小程序所有页面的顶部背景颜色,文字颜色定义等;
- 详情见 微信官方文档全局配置
-
工具配置 project.config.json
- 类似于androidev 中的gradlewrapper ,同步化的个性化配置;
- 详情见 微信官方文档项目配置文件
-
页面配置 page.json
- 指的是
pages/logs
中的logs.json这类和小程序页面相关的配置; - 可以让开发者独立定义每个页面的一些属性,如顶部颜色,是否允许下拉刷新等,app.json是定义整个小程序的配置;
- 详情见 微信官方文档页面配置文件
- 指的是
###WXML 模板文件 weixin markup language
在小程序中WXML相当于网页开发中的HTML;不同点在于:
- 标签名字不同 使用的是单独包装好的标签;
- 添加
wx:if
这样的属性以及类似于{{}}
表达式;MVVM
的开发模,将渲染和逻辑分离,数据绑定;- 不让js直接操控dom,通过一种模板语法描述状态和界面结构的关系;
- 详情见 微信官方文档WXML
###WXSS 样式文件 weixin style sheet
类似于网页开发中的css; 扩展:
- 尺寸单位; (iphone6位视觉稿的标准)
- wxss 在底层支持新的尺寸单位
rpx
;(responsive pixel)可以根据屏幕宽度进行自适应;
- wxss 在底层支持新的尺寸单位
- 样式导入;
@import
导入外联样式表, 外联样式表的相对路径;- 提供全局样式和局部样式; 可定义一个
app.wxss
表示全局样式;局部样式page.wxss
仅对当前页面生效;
- 内联样式
- 组件上使用style和class属性控制组件的样式;
- style : 静态的样式统一写到class中;而style接受动态的样式,在运行时会进行解析;
- class : 用于指定样式规则,属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.;样式类名间用空格隔开;
- js 中选择器基本支持;
- 组件上使用style和class属性控制组件的样式;
- 详情见 微信官方文档WXSS
/** common.wxss **/
.small-p {
padding:5px;
}
/** app.wxss **/
@import "common.wxss";
.middle-p {
padding:15px;
}
###wxs 小程序脚本语言; weixin script
可构建出页面的结构;
- 不依赖与运行时的基础库版本,可以在所有版本的小程序汇总运行;
- wxs的运行环境不能调用其他javascript文件中定义的函数;
- wxs函数不能作为组件的事件回调;
- 详情见 微信官方文档Wxs
// 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>
###JS 脚本逻辑文件 javascript
用于和用户交互;
- 可用js调用小程序提供的API,调起微信提供的能力;
- 详情见 微信官方文档API
小程序宿主环境
小程序的运行环境可分为 渲染层(View)和逻辑层(App Service); wxml和wxss工作在渲染层,js脚本工作在逻辑层; 分别由两个线程管理,渲染层的界面使用webview 进行渲染;逻辑层采用JsCore线程运行JS脚本;
小程序存在多个界面,渲染层存在有多个webview线程,这两个线程的通信会经由微信客户端(Native代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发;
程序和页面
-
wx客户端 在打开小程序之前,会把整个小程序的代码包下载到本地;
-
app.json
的pages字段可以知道当前小程序的所有页面路径; wx客户端把首页的代码装载进来,通过底层的一些机制,渲染整个首页;- 第一个页面就是小程序的首页;
-
小程序启动后,
app.js
定义的app实例 的onLaunch
回调会被执行; 整个小程序只有一个app实例 是全部页面共享的;- app.json 中定义的路径中都包含了4种文件,分别为
js,json,wxml,wxss
首先wx客户端会根据json配置生成一个界面,配置整体页面的颜色和文字; - 然后wx客户端根据 wxml和wxss文件装载样式;
- 最后wx客户端 装载 js文件;
- app.json 中定义的路径中都包含了4种文件,分别为
-
页面 js文件中
Page
是一个页面构造器,这个构造器就生成了一个页面; 在生成页面时,小程序框架会把data数据和index.wxml 一起渲染出最终的结构; 在渲染完界面之后,页面实例就会收到一个onLoad
的回调,可以在这个回调中处理你的逻辑;
###目录结构
app : 描述整体程序;
page : 描述各自页面;
一个小程序主体部分构成: app.js, app.json, app.wxss;
一个小程序页面构成: js, wxml, json, wxss; 描述页面的四个文件必须具有相同的路径和文件名;
sitemap 小程序内搜索
小程序根目录下 sitemap.json 文件用来配置小程序及其页面是否允许被微信索引;
默认是都能被索引;
小程序框架 (逻辑层 + 视图层)
###多个简便写法
- behaviors 的使用 ,pages可引用behaviors用来让更多的页面有相同的数据字段和方法;
- Component 构造器构造页面 ,适合于复杂的页面; Page构造器适用于简单的页面构建;
小程序页面生命周期
###页面路由
框架进行页面的路由管理,以栈的形式维护了当前的所有页面;
路由方式 | 页面栈表现 | 触发时机 | 路由前页面 | 路由后页面 |
---|---|---|---|---|
初始化 | 新页面入栈 | 小程序打开的第一个页面 | onLoad, onShow | |
打开新页面 | 新页面入栈 | 调用 API wx.navigateTo 使用组件 <navigator open-type="navigateTo"/> | onHide | onLoad, onShow |
页面重定向 | 当前页面出栈,新页面入栈 | 调用 API wx.redirectTo 使用组件 <navigator open-type="redirectTo"/> | onUnload | onLoad, onShow |
页面返回 | 页面不断出栈,直到目标返回页 | 调用 API wx.navigateBack 使用组件<navigator open-type="navigateBack"> 用户按左上角返回按钮 | onUnload | onShow |
Tab切换 | 页面全部出栈,只留下新的tab页面 | 调用 API wx.switchTab 使用组件 <navigator open-type="switchTab"/> 用户切换 Tab | 各种情况请参考下表 | |
重加载 | 页面全部出栈,只留下新的页面 | 调用 API wx.reLaunch 使用组件 <navigator open-type="reLaunch"/> | onUnload | onLoad, onShow |
getCurrentPages()
获取当前页面栈;
tips:
- navigateTo,redirectTo 只能打开非tabBar页面;
- switchTab 只能打开tabBar页面;
- relaunch 可以打开任意页面;
- 页面底部的tabBar由页面决定,即只要是定义为tabBar的页面,底部都有tabBar;
- 调用页面路由自带的参数可以在目标页面的onLoad中获取;
###模块化 (相当于library或者工具类)
将公共的代码抽离成为一个单独的js文件,作为一个模块; 模块只需要通过module.exports
或者exports
才能对外暴露接口;
- exports 是 module.exports 的一个引用,因此在模块里面随意更改exports 的 指向 会造成未知的错误; 推荐使用
module.exports
来暴露接口; 在使用这些模块文件中,使用require
将公共代码引入; - 小程序目前不支持直接引入 node_modules , 开发者需要使用到 node_modules 时候建议拷贝出相关的代码到小程序的目录中,或者使用小程序支持的 npm 功能。
// common.js
function sayHello(name) {
console.log(`Hello ${name} !`)
}
function sayGoodbye(name) {
console.log(`Goodbye ${name} !`)
}
module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye
---------------------
var common = require('common.js')
Page({
helloMINA: function() {
common.sayHello('MINA')
},
goodbyeMINA: function() {
common.sayGoodbye('MINA')
}
})
###微信原生API
- 类型
-
事件监听API
on
开头的api 用来监听某个事件是否触发,这类api接受一个回调函数作为参数,当事件触发时会调用这个回调函数,并将相关数据以参数形式传入;- wx.onCompassChange
-
同步API
Sync
结尾的api 通过函数返回值直接获取,执行出错会抛出异常; 需要try catch;- wx.setStorageSync
-
异步API
- 这类api 通常接受一个Object类型参数,这个参数都支持按需指定以下字段来接受接受调用结果;
- Object参数: success,fail,complete,其他;
- 回调函数的参数: success,fail,complete函数调用时传入一个Object类型参数,包括以下字段: errMsg:String,errCode:number,其他;
- 异步api返回promise
- 2.10.2 ,异步api支持callback & promise 两种调用方式;当接口参数Object对象中不包含success/fail/complete时将默认返回promise,否则仍按回调方式执行,无返回值;
- 部分接口如 downloadFile, request, uploadFile, connectSocket, createCamera(小游戏)本身就有返回值, 它们的 promisify 需要开发者自行封装。
- 当没有回调参数时,异步接口返回 promise。此时若函数调用失败进入 fail 逻辑, 会报错提示 Uncaught (in promise),开发者可通过 catch 来进行捕获。
- wx.onUnhandledRejection 可以监听未处理的 Promise 拒绝事件。
- 这类api 通常接受一个Object类型参数,这个参数都支持按需指定以下字段来接受接受调用结果;
-
wx.login({
success(res) {
console.log(res.code)
}
})
<!-- callback 形式调用 -->
wx.chooseImage({
success(res) {
console.log('res:', res)
}
})
<!-- promise 形式调用 -->
wx.chooseImage().then(res => console.log('res: ', res))
###事件系统
- 视图层到逻辑成的通讯方式;
- 可以绑定在组件上,当达到触发事件,执行逻辑层中对应的事件处理函数;
- 事件对象可以携带额外信息,如id,dataset,touches;
使用方式:
组件中绑定一个事件处理函数; bindtap
在页面对应的page中找到对应的事件处理函数; 后期可以改为 bind:tap
wxs函数响应事件:
wxs函数接受2个参数,第一个是event
,在原有的event的基础上加event.instance对象; 第二个参数是ownerInstance
,和event.instance一样是一个ComponentDescriptor
对象;
event.instance 表示触发事件的组件的ComponentDescriptor实例;
ownerInstance 表示的是触发事件的组件所在的组件的 ComponentDescriptor实例;如果触发事件的组件是在页面内的,则表示的是页面实例;
wxs运行在视图层(webview),需要有一个机制和逻辑层开发者(App Service)的代码通信,ComponentDescriptor 的callMethod
是wxs里面调用逻辑层(appservice)定义的方法;
WxsPropObserver
是逻辑层(appservice)调用wxs逻辑的机制; wxs函数必须由{{}}
括起来;当 prop 的值被设置 WXS 函数就会触发,而不只是值发生改变,所以在页面初始化的时候会调用一次WxsPropObserver的函数。
--------
<wxs module="wxs" src="./test.wxs"></wxs>
<view id="tapTest" data-hi="WeChat" bindtap="{{wxs.tapName}}"> Click me! </view>
**注:绑定的WXS函数必须用{{}}括起来**
--------
function tapName(event, ownerInstance) {
console.log('tap wechat', JSON.stringify(event))
}
module.exports = {
tapName: tapName
}
//`change:prop`是在prop属性被设置的时候触发wxs函数;
<wxs module="test" src="./test.wxs"></wxs>
<view change:prop="{{test.propObserver}}" prop="{{propValue}}" bindtouchmove="{{test.touchmove}}" class="movable"></view>
module.exports = {
touchmove: function(event, instance) {
console.log('log event', JSON.stringify(event))
},
propObserver: function(newValue, oldValue, ownerInstance, instance) {
console.log('prop observer', newValue, oldValue)
}
}
---------
//阻止事件冒泡 点击内部只有handTap3和handTap2会响应;
<view id="outer" bindtap="handleTap1">
outer view
<view id="middle" catchtap="handleTap2">
middle view
<view id="inner" bindtap="handleTap3">
inner view
</view>
</view>
</view>
//互斥事件,点击内部 handleTap3和handleTap2会响应;点击middle,响应handTap2和handTap1;
<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>
//捕获事件,点击内部 hand2 -> hand4 -> hand3 ->hand1
<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>
-
事件分类
- 冒泡事件 当一个组件上的事件被触发后,该事件会向父节点传递;
- 包括触摸,点击,动画等; touchstart,touchmove,touchcancel,touchend,tap,longpress,longtap,transitionend,animationstart,animationiteration,animationend,touchforcechange;
- 非冒泡事件 当一个组件上的事件被触发后,该事件不会向父节点传递;
- 绑定并阻止事件冒泡 ,除
bind
外,也可以用catch
绑定事件; catch 会阻止事件向上冒泡; - 互斥事件绑定 ,
mut-bind
如果事件冒泡到其他节点上,其他节点的mut-bind绑定函数不会被触发,但bind绑定和catch的绑定函数依旧会被触发; - 事件的捕获阶段,触摸类事件支持捕获阶段,捕获阶段位于冒泡阶段之前,且在捕获阶段中事件到达节点的顺序与冒泡阶段刚好相反;
capture-bind
,capture-catch
后者将中断捕获阶段和取消冒泡阶段;
- 冒泡事件 当一个组件上的事件被触发后,该事件会向父节点传递;
-
事件对象
触发事件时,逻辑层绑定该事件的处理函数会受到一个事件对象,BaseEvent / customEvent(detail) / touchevent(touches/changedTouches) ; 组成:
- type : 事件类型;
- timeStamp : 页面打开到触发事件所经过的毫秒数;
- target : 触发事件的源组件的一些属性值集合; id/dataset (冒泡)
- currentTarget : 事件绑定的当前组件; id/dataset
- dataset : wxml 自定义数据以
data-
开头,连字符写法最后会转为驼峰写法;
- dataset : wxml 自定义数据以
- mark : 识别具体触发事件的target节点; 可承载一些自定义数据(类似于dataset) ,冒泡路径的mark会被合并并返回;
- mark和dataset很相似,区别在于:
mark
会包含从触发事件的节点到根节点上所有的mark:
属性值; 而dataset
仅包含一个节点的data-
属性值; - 如果存在同名的mark,父节点的mark会被子节点的mark覆盖;
- 自定义组件中接受事件,mark不包含自定义组件外的节点mark;
- 不同于dataset, 节点的mark不会做连字符和大小写转换;
- mark和dataset很相似,区别在于:
<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 // 大写会转为小写
}
})
---------
<view mark:myMark="last" bindtap="bindViewTap">
<button mark:anotherMark="leaf" bindtap="bindButtonTap">按钮</button>
</view>
Page({
bindViewTap: function(e) {
e.mark.myMark === "last" // true
e.mark.anotherMark === "leaf" // true
}
})
###组件
组件时视图层的基本组成单元;组件包括一个开始标签和结束标签,属性用来修饰这个组件,内容在两个标签之间;
属性类型: boolean,number,string,array,object,eventHandler,any;
公共属性:
- id String 组件的唯一标志;
- class String 组件的样式类;wxss
- style String 组件的内联样式;
- hidden boolean 组件是否显示;
- data-* any 自定义属性;
- bind*/catch* EventHandler 组件的事件;
###动画
- Css渐变,Css动画
- 监听 (都不是冒泡事件)
- bindtransitionend(CSS 渐变结束或wx.createAnimation 结束一个阶段)
- bindanimationstart(CSS 动画开始)
- bindanimationiteration(CSS 动画结束一个阶段)
- bindanimationend( CSS 动画结束)
- 监听 (都不是冒泡事件)
- 关键帧动画 替换wx.createAnimation
- this.animate接口 this.animate(selector, keyframes, duration, callback)
- selector :String 选择器(selectorQuery.select选择器样式)
- keyframes :Array 关键帧;
- duration :Number 动画持续时长ms;
- callback :Function (nullable) 动画完成后的回调函数;
- ScrollTimeline 参数 (仅支持scroll-view),绑定滚动元素;
- this.clearAnimation(selector, options, callback) 清除属性;
- options :Object 需要清除的属性,不填写则全部清除;
- this.animate接口 this.animate(selector, keyframes, duration, callback)
- 高级动画(属性动画) wxs响应事件方式可以通过使用wxs来响应事件的方法动态调整节点的style属性; 不断变更style属性可做到动画效果; 推荐将页面的setData改为自定义组件中的setData提升性能;
this.animate('#container', [
{ opacity: 1.0, rotate: 0, backgroundColor: '#FF0000' },
{ opacity: 0.5, rotate: 45, backgroundColor: '#00FF00'},
{ opacity: 0.0, rotate: 90, backgroundColor: '#FF0000' },
], 5000, function () {
this.clearAnimation('#container', { opacity: true, rotate: true }, function () {
console.log("清除了#container上的opacity和rotate属性")
})
}.bind(this))
this.animate('.block', [
{ scale: [1, 1], rotate: 0, ease: 'ease-out' },
{ scale: [1.5, 1.5], rotate: 45, ease: 'ease-in', offset: 0.9},
{ scale: [2, 2], rotate: 90 },
], 5000, function () {
this.clearAnimation('.block', function () {
console.log("清除了.block上的所有动画属性")
})
}.bind(this))
###重启策略和更新机制
重启策略
pages 对应的json文件中配置(也可以配置在app.json的window字段中),指定restartStrategy
配置项,使得某个页面退出后,下次A类场景(不包括重定向和冷启动下带path跳入的场景) 的冷启动可以回到这个页面;
注意: 但不包括退出过久的情况,因为存在退出状态的保存
中有expireTimeStamp 字段哟用户超时丢弃数据;
{
"restartStrategy": "homePage"
}
- homePage: (默认值)如果从这个页面退出小程序,下次将从首页冷启动
- homePageAndLatestPage: 如果从这个页面退出小程序,下次冷启动后立刻加载这个页面,页面的参数保持不变(不可用于 tab 页)
退出状态
: 类似于android的保存状态 ,小程序可能销毁时调用onSaveExitState
保存页面状态,下次启动时刻通过exitState
获得这些数据;
onSaveExitState
返回值包括两项: data:Any
,expireTimeStamp:Number
(默认当前时刻+1天)
更新机制
- 未启动时更新
wx client会有若干个时机检查本地缓存的小程序有没有更新版本,如果有会静默更新; 最差的情况下 发布之后24小时内更新;
- 启动时更新
使用 wx.getUpdataManager
Api
自定义组件
类似于页面,一个自定义组件由 json,wxml,wxss,js
;
要编写一个自定义组件, 首先需要在
json
文件中进行自定义组件声明(将component
字段设为true 可将一组文件设为自定义组件;)
{
"component": true
}
wxml
文件中加入组件样式, 在wxss
文件中加入组件样式;
- 在组件wxss 中不应使用id选择器,属性选择器和标签名选择器,使用class选择器;
- 组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,避免使用;
- 子元素选择器(.a > .b)只能用于view组件和其子节点之间,用于其他组件会导致非预期情况;
- 继承样式,如font,color,会从组件外继承到组件内;
- 除继承样式外,app.wxss中的样式,组件所在页面的样式对自定义组件无效(除非更改组件样式隔离选项)
- 默认情况下,自定义组件的样式只受到自定义组件的wxss的影响,除非:
- app.wxss或页面的wxss中使用了标签名选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件;通常不推荐;
- 指定特殊的样式隔离选项
styleIsolation
- isolated: 表示启用样式隔离,使用class指定的样式将不会相互影响(一般情况下的默认值)
- apply-shared: 表示页面wxss样式将影响到自定义组件,但自定义组件wxss中指定的样式不会影响页面;
- shared: 表示页面wxss样式将影响到自定义组件,自定义组件wxss中指定的样式也会影响页面和其他设置了apply-shared或shared 的自定义组件(这个选项在插件中不可用;)
- 如果这个
Component
构造器用于构造页面,默认值为shared;且还有额外的隔离选项可用:- page-isolated: 表示在这个页面禁用app.wxss,同时页面的wxss不会影响到其他自定义组件;
- page-apply-shared: 表示在这个页面禁用app.wxss,同时页面的wxss不会影响到其他自定义组件,但设为shared的自定义组件会影响到页面;
- page-shared: 表示在这个页面禁用app.wxss,同时页面wxss样式会影响到其他设为apply-shared 或 shared 的自定义组件,也会受到设为shared的自定义组件的影响;
- 在2.10.1开始,也可以在页面或自定义组件的json文件配置
styleIsolation
(这样不需在js中的options中再配置) - 在2.2.3以上,支持
addGlobalClass
选项,即在Component的options中设置addGlobalClass:true
; 这个选项等价于设置styleIsolation:apply-shared
,但设置了styleIsolation选项后这个选项会失效;
- 默认情况下,自定义组件的样式只受到自定义组件的wxss的影响,除非:
- 除此之外,组件可以指定它所在节点的默认样式,使用
:host
选择器 - 外部样式类,可在component 中用
externalClasses
定义段定义若干个外部样式类; 避免同一个节点上使用普通样式类和外部样式类; - 即时启动了样式隔离
isolated
,组件仍然可以在局部引用组件所在页面的样式或父组件的样式; 可以使用~
引用所在页面的样式; 可以使用^
引用父组件的样式; 如果组件是比较独立通用的组件,优先使用外部样式类的方式;
在组件模板中可以提供一个 <slot>
节点,用于承载组件引用时提供的子节点。 默认情况下,一个组件wxml中只能有一个slot,需要使用多slot时,可以在组件js中声明启用;
<!-- 组件模板 -->
<view class="wrapper">
<slot name="before"></slot>
<view>这里是组件的内部节点</view>
<slot name="after"></slot>
</view>
<!-- 引用组件的页面模板 -->
组件的属性 propA 和 propB 将收到页面传递的数据。页面可以通过 setData 来改变绑定的数据字段
<view>
<component-tag-name prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}">
<view slot="before">这里是插入到组件slot name="before"中的内容</view>
<!-- 这部分内容将被放置在组件 <slot> 的位置上 -->
<view slot="after">这里是插入到组件slot name="after"中的内容</view>
</component-tag-name>
</view>
//引用外部样式:
/* 组件 custom-component.js */
Component({
externalClasses: ['my-class']
})
<!-- 组件 custom-component.wxml -->
<custom-component class="my-class">这段文本的颜色由组件外的 class 决定</custom-component>
<!-- 页面的 WXML 使用my-class的样式-->
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />
<!-- 以下写法需要基础库版本 2.7.1 以上 -->
<custom-component my-class="red-text large-text" />
在自定义组件的js文件中,需要使用
component
来注册组件,并提供组件的属性定义,内部数据和自定义方法;
Component({
options:{
multipleSlots:true, // 在组件定义时的选项中启用多slot支持
styleIsolation:'isolated'
}
properties: {
// 这里定义了innerText属性,属性值可以在组件使用时指定
innerText: {
type: String,
value: 'default value',
}
},
data: {
// 这里是一些组件内部数据
someData: {}
},
methods: {
// 这里是一个自定义方法
customMethod: function(){}
}
})
使用自定义组件,首先在
页面的json
文件中进行引用声明;
- 需要提供每个自定义组件的标签名和对应的自定义组件文件路径;
- 引用声明后,就可以在wxml中使用基础组件一样使用自定义组件;节点名即自定义组件的标签名,节点属性即传递给组件的属性值;
{
"usingComponents": {
"component-tag-name": "path/to/the/custom/component"
}
}
tips:
- 因为 WXML 节点标签名只能是小写字母、中划线和下划线的组合,所以自定义组件的标签名也只能包含这些字符。
- 自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用 usingComponents 字段)。
- 自定义组件和页面所在项目根目录名不能以“wx-”为前缀,否则会报错。
- 是否在页面文件中使用 usingComponents 会使得页面的 this 对象的原型稍有差异
- 使用 usingComponents 页面的原型与不使用时不一致,即 Object.getPrototypeOf(this) 结果不同。
- 使用 usingComponents 时会多一些方法,如 selectComponent 。
- 出于性能考虑,使用 usingComponents 时, setData 内容不会被直接深复制,即 this.setData({ field: obj }) 后 this.data.field === obj 。(深复制会在这个值被组件间传递时发生。)
Component 构造器
Component 构造器可用于定义组件,调用Component构造器可以指定组件的属性,数据,方法等;
Component({
behaviors: [],
properties: {
myProperty: { // 属性名
type: String,
value: ''
},
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) {
}
}
})
使用Component
构造器构造页面 ,代替 Page
,需要在json中包含usingComponents
定义段; 使用Component构造器来构造页面的一个好处是可以使用behaviors
提取所有页面中公用的代码段; 可以在所有页面被创建和销毁时都会执行同一段代码,可以提取到behaviors;
注意: behavior
如果有同名的属性或方法,组件本身的属性或方法会覆盖behavior中的属性或方法;多个behavior,靠后的覆盖前的; 同名的数据字段如果是对象进行合并,非对象互相覆盖; 生命周期函数不会相互覆盖,而是在对应触发事件被逐个调用; 同一个behavior被一个组件多次引用,定义的生命周期函数只会被执行一次;
{
"usingComponents": {}
}
// page-common-behavior.js
module.exports = Behavior({
attached: function() {
// 页面创建时执行
console.info('Page loaded!')
},
detached: function() {
// 页面销毁时执行
console.info('Page unloaded!')
}
})
// 页面 A
var pageCommonBehavior = require('./page-common-behavior')
Component({
behaviors: [pageCommonBehavior],
data: { /* ... */ },
methods: { /* ... */ },
})
触发事件: triggerEvent
方法,指定事件名,detail对象和事件选项; 支持冒泡和捕获处理:
- bubbles: Boolean 事件是否冒泡;
- composed:Boolean 事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部;true ,可以进入组件内部事件;
- capturePhase:Boolean 事件是否拥有捕获阶段;
- wx://form-field : 类似于表单控件的行为;
- wx://form-field-group : 使 form 组件可以识别到这个自定义组件内部的所有表单控件。
- wx://component-export : 使自定义组件支持 export 定义段。这个定义段可以用于指定组件被 selectComponent 调用时的返回值。
Component({
behaviors: ['wx://form-field']
})
组件间关系
关联
relations
包含目标组件路径及其对应选项;
- type : String 目标组件的相对关系,必填,可选值:parent/child/ancestor/descendant;
- linked:Function 关系生命周期函数,当关系被建立在页面节点树中时触发,触发时机在组件attached生命周期之后
- linkChanged:Function 关系生命周期函数,当关系在页面节点树中发生改变时触发,触发时机在组件moved生命周期之后
- unlinked:Function 关系生命周期函数,当关系脱离页面节点树时触发,触发时机在组件detached生命周期之后
- target:String 如果这一项被设置,则它表示关联的目标节点所应具有的behavior,所有拥有这一behavior的组件节点都会被关联
数据监听器
observers
**
通配符 表示监听所有子数据字段的变化; 如’some.field.**'监听前缀;
纯数据字段
纯数据字段是一些不用于界面渲染的 data 字段,可以用于提升页面更新性能。 wxml不会渲染;
Component构造器的options定义段中指定pureDataPattern为一个正则表达式;字段名符合这个正则则成为纯数据字段;
Component({
options: {
pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段
},
data: {
a: true, // 普通数据字段
_b: true, // 纯数据字段
},
methods: {
myMethod() {
this.data._b // 纯数据字段可以在 this.data 中获取
this.setData({
c: true, // 普通数据字段
_d: true, // 纯数据字段
})
}
}
})
<view wx:if="{{a}}"> 这行会被展示 </view>
<view wx:if="{{_b}}"> 这行不会被展示 </view>
抽象节点
自定义组件模板中的一些节点,对应的自定义组件不是由自定义组件本身确定的,而是自定义组件的调用者确定的;
<!-- selectable-group.wxml -->
<view wx:for="{{labels}}">
<label>
<selectable disabled="{{false}}"></selectable>
{{item}}
</label>
</view>
“selectable”不是任何在 json 文件的 usingComponents 字段中声明的组件,而是一个抽象节点。它需要在 componentGenerics 字段中声明:
{
"componentGenerics": {
"selectable": true
}
}
<selectable-group generic:selectable="custom-radio" /> 指定抽象节点为custom-radio; (也需要注册在Components) `generic:xxx="yyy"`值只能是静态值,不能包含数据绑定;抽象节点不适用于动态决定节点名的场景;
{
"usingComponents": {
"custom-radio": "path/to/custom/radio"
}
}
自定义组件扩展
definitionFilter
提供修改自定义组件定义段的能力,用于支持自定义组件扩展; definitionFilter
是一个函数,是被调用时会被注入两个参数,第一个参数是使用该behavior的component/behavior的定义对象;第二个参数是该behavior所使用的behavior的definitionFilter函数列表;