小程序界面图标(走进小程序【六】微信小程序架构之【视图层】万字详解)
🌟前言
哈喽小伙伴们 ,上一期为大家讲解了一下小程序的架构之【逻辑层】篇 。今天接着上篇 ,给大家结合官方文档讲解一下小程序的架构之【视图层】;今天的文章有点长 ,很多知识点也是官方文档给我们提供的;也是想告诉大家 ,总有一天我们进入职场后 ,会甩开我们的拐棍或者离开我们的靠山独自去面对困难; 所以养成阅读官方文档的好习惯对我们日后工作也有很大的好处 。好了 ,让我们一起来看看吧🤘
🌟小程序架构
小程序开发框架的目标是通过尽可能简单 、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务 。
整个小程序框架系统分为两部分:逻辑层(App Service)和 视图层(View) 。小程序提供了自己的视图层描述语言 WXML 和 WXSS ,以及基于 JavaScript 的逻辑层框架 ,并在视图层与逻辑层间提供了数据传输和事件系统 ,让开发者能够专注于数据与逻辑 。
View 层用来渲染页面结构 ,AppService 层用来逻辑处理 、数据请求 、接口调用 。
它们在两个线程里运行 。
视图层和逻辑层通过系统层的 JSBridage
进行通信 ,逻辑层把数据变化通知到视图层 ,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理 视图层使用 WebView 渲染 ,iOS中使用自带 WKWebView ,在 Android 使用腾讯的 x5内核(基于 Blink)运行 。 逻辑层使用在 iOS 中使用自带的 JSCore 运行,在 Android中使用腾讯的 x5 内核(基于 Blink)运行 。 开发工具使用 nw.js 同时提供了视图层和逻辑层的运行环境 。🌟视图层 View
框架的视图层由 WXML 与 WXSS 编写 ,由组件来进行展示 。
将逻辑层的数据反映成视图 ,同时将视图层的事件发送给逻辑层 。
WXML(WeiXin Markup language) 用于描述页面的结构。
WXS(WeiXin Script) 是小程序的一套脚本语言 ,结合 WXML ,可以构建出页面的结构 。
WXSS(WeiXin Style Sheet) 用于描述页面的样式 。
组件(Component)是视图的基本组成单元。
🌟WXML
WXML(WeiXin Markup Language)是框架设计的一套标签语言 ,结合基础组件 、事件系统 ,可以构建出页面的结构 。
要完整了解 WXML 语法 ,请参考WXML 语法参考 。
用以下一些简单的例子来看看 WXML 具有什么能力:
🌟数据绑定
<!--wxml--> <view> {{message}} </view> // page.js Page({ data: { message: Hello MINA! } })🌟列表渲染
<!--wxml--> <view wx:for="{{array}}"> {{item}} </view> // page.js Page({ data: { array: [1, 2, 3, 4, 5] } })🌟条件渲染
<!--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 } })🌟模板
<!--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} } })🌟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 中 ,以免影响渲染速度 。 class:用于指定样式规则 ,其属性值是样式规则中类选择器名(样式类名)的集合 ,样式类名不需要带上. ,样式类名之间用空格分隔 。 <view class="normal_view" />🌟选择器
目前支持的选择器有:
选择器 样例 样例描述 .class .intro 选择所有拥有 class=“intro ” 的组件 #id #firstname 选择拥有 id=“firstname ” 的组件 element 选择所有 view 组件 element, element view, checkbox 选择所有文档的 view 组件和所有的 checkbox 组件 ::after view::after 在 view 组件后边插入内容 ::before view::before 在 view 组件前边插入内容🌟全局样式与局部样式
定义在 app.wxss 中的样式为全局样式 ,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式 ,只作用在对应的页面 ,并会覆盖 app.wxss 中相同的选择器 。
🌟WXS
WXS(WeiXin Script)是小程序的一套脚本语言 ,结合 WXML,可以构建出页面的结构 。
注意事项
WXS 不依赖于运行时的基础库版本 ,可以在所有版本的小程序中运行 。 WXS 与 JavaScript 是不同的语言 ,有自己的语法,并不和 JavaScript 一致 。 WXS 的运行环境和其他 JavaScript 代码是隔离的 ,WXS 中不能调用其他 JavaScript 文件中定义的函数 ,也不能调用小程序提供的API 。 由于运行环境的差异 ,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍 。在 android 设备上二者运行效率无差异 。以下是一些使用 WXS 的简单示例 ,要完整了解 WXS 语法 ,请参考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
🌟事件
🌟什么是事件
事件是视图层到逻辑层的通讯方式 。 事件可以将用户的行为反馈到逻辑层进行处理 。 事件可以绑定在组件上 ,当达到触发事件 ,就会执行逻辑层中对应的事件处理函数 。 事件对象可以携带额外信息 ,如 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) } }) 可以看到log出来的信息大致如下: { "type":"tap", "timeStamp":895, "target": { "id": "tapTest", "dataset": { "hi":"Weixin" } }, "currentTarget": { "id": "tapTest", "dataset": { "hi":"Weixin" } }, "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 }] }🌟使用WXS函数响应事件
从基础库版本2.4.4开始 ,支持使用WXS函数绑定事件 ,WXS函数接受2个参数,第一个是event ,在原有的event的基础上加了event.instance对象 ,第二个参数是ownerInstance ,和event.instance一样是一个ComponentDescriptor对象。具体使用如下:
在组件中绑定和注册事件处理的WXS函数 。 <wxs module="wxs" src="./test.wxs"></wxs> <view id="tapTest" data-hi="Weixin" bindtap="{{wxs.tapName}}"> Click me! </view>注:绑定的WXS函数必须用{{}}括起来
test.wxs文件实现tapName函数 function tapName(event, ownerInstance) { console.log(tap Weixin, JSON.stringify(event)) } module.exports = { tapName: tapName }ownerInstance包含了一些方法 ,可以设置组件的样式和class ,具体包含的方法以及为什么要用WXS函数响应事件 ,请点击查看详情 。
🌟事件详解
🌟事件分类
事件分为冒泡事件和非冒泡事件:
冒泡事件:当一个组件上的事件被触发后 ,该事件会向父节点传递 。 非冒泡事件:当一个组件上的事件被触发后 ,该事件不会向父节点传递 。WXML的冒泡事件列表:
类型 触发条件 最低版本 touchstart 手指触摸动作开始 touchmove 手指触摸后移动 touchcancel 手指触摸动作被打断 ,如来电提醒 ,弹窗 touchend 手指触摸动作结束 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事件 ,(详见各个组件)
🌟普通事件绑定
事件绑定的写法类似于组件的属性 ,如:
<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 会先后调用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>🌟互斥事件绑定
自基础库版本 2.8.2 起 ,除 bind 和 catch 外 ,还可以使用 mut-bind 来绑定事件 。一个 mut-bind 触发后 ,如果事件冒泡到其他节点上 ,其他节点上的 mut-bind 绑定函数不会被触发 ,但 bind 绑定函数和 catch 绑定函数依旧会被触发。
换而言之 ,所有 mut-bind 是“互斥 ”的 ,只会有其中一个绑定函数被触发 。同时,它完全不影响 bind 和 catch 的绑定效果 。
例如在下边这个例子中 ,点击 inner view 会先后调用 handleTap3 和 handleTap2 ,点击 middle view 会调用 handleTap2 和 handleTap1 。
<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-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 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 中,如果按钮被点击 ,将触发 bindViewTap 和 bindButtonTap 两个事件 ,事件携带的 event.mark 将包含 myMark 和 anotherMark 两项 。
Page({ bindViewTap: function(e) { e.mark.myMark === "last" // true e.mark.anotherMark === "leaf" // true } })mark 和 dataset 很相似,主要区别在于: mark 会包含从触发事件的节点到根节点上所有的 mark: 属性值;而 dataset 仅包含一个节点的 data- 属性值 。
细节注意事项:
如果存在同名的 mark ,父节点的 mark 会被子节点覆盖 。 在自定义组件中接收事件时 , mark 不包含自定义组件外的节点的 mark 。 不同于 dataset ,节点的 mark 不会做连字符和大小写转换 。 🌟touchestouches 是一个数组 ,每个元素为一个 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轴 🌟CanvasTouch changedToucheschangedTouches 数据格式同 touches。 表示有变化的触摸点,如从无变有(touchstart) ,位置变化(touchmove) ,从有变无(touchend 、touchcancel) 。
🌟detail自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入 ,媒体的错误事件会携带错误信息 ,详见组件定义中各个事件的定义 。
点击事件的detail 带有的 x, y 同 pageX, pageY 代表距离文档左上角的距离。
🌟WXS响应事件
🌟背景
有频繁用户交互的效果在小程序上表现是比较卡顿的 ,例如页面有 2 个元素 A 和 B ,用户在 A 上做 touchmove 手势 ,要求 B 也跟随移动 ,movable-view 就是一个典型的例子 。一次 touchmove 事件的响应过程为:
a 、touchmove 事件从视图层(Webview)抛到逻辑层(App Service)
b 、逻辑层(App Service)处理 touchmove 事件 ,再通过 setData 来改变 B 的位置
一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染 ,通信的耗时比较大 。此外 setData 渲染也会阻塞其它脚本执行 ,导致了整个用户交互的动画过程会有延迟 。
🌟实现方案
本方案基本的思路是减少通信的次数 ,让事件在视图层(Webview)响应 。小程序的框架分为视图层(Webview)和逻辑层(App Service) ,这样分层的目的是管控 ,开发者的代码只能运行在逻辑层(App
Service),而这个思路就必须要让开发者的代码运行在视图层(Webview) ,如下图所示的流程:使用 WXS 函数用来响应小程序事件 ,目前只能响应内置组件的事件,不支持自定义组件事件 。WXS 函数的除了纯逻辑的运算 ,还可以通过封装好的ComponentDescriptor 实例来访问以及设置组件的 class 和样式 ,对于交互动画 ,设置 style 和 class 足够了 。WXS 函数的例子如下:
var wxsFunction = function(event, ownerInstance) { var instance = ownerInstance.selectComponent(.classSelector) // 返回组件的实例 instance.setStyle({ "font-size": "14px" // 支持rpx }) instance.getDataset() instance.setClass(className) // ... return false // 不往上冒泡 ,相当于调用了同时调用了stopPropagation和preventDefault }其中入参 event 是小程序事件对象基础上多了 event.instance 来表示触发事件的组件的 ComponentDescriptor 实例 。ownerInstance 表示的是触发事件的组件所在的组件的
ComponentDescriptor 实例 ,如果触发事件的组件是在页面内的 ,ownerInstance 表示的是页面实例 。ComponentDescriptor的定义如下:
方法 参数 描述 最低版本 selectComponent selector对象 返回组件的 ComponentDescriptor 实例 。 selectAllComponents selector对象数组 返回组件的 ComponentDescriptor 实例数组 。 setStyle Object/string 设置组件样式 ,支持rpx 。 设置的样式优先级比组件 wxml 里面定义的样式高 。不能设置最顶层页面的样式。 addClass/removeClass/hasClass string 设置组件的 class 。设置的 class 优先级比组件 wxml 里面定义的 class 高 。不能设置最顶层页面的 class。 getDataset 无 返回当前组件/页面的 dataset 对象 callMethod (funcName:string, args:object) 调用当前组件/页面在逻辑层(App Service)定义的函数 。funcName表示函数名称 ,args表示函数的参数 。 requestAnimationFrame Function 和原生 requestAnimationFrame 一样 。用于设置动画 。 getState 无 返回一个object对象 ,当有局部变量需要存储起来后续使用的时候用这个方法 。 triggerEvent (eventName, detail) 和组件的triggerEvent一致 。 getComputedStyle Array.<string> 参数与 SelectorQuery 的 computedStyle 一致 。 2.11.2 setTimeout (Function, Number) 与原生 setTimeout 一致 。用于创建定时器 。 2.14.2 clearTimeout Number 与原生 clearTimeout 一致 。用于清除定时器 。 2.14.2 getBoundingClientRect 无 返回值与 SelectorQuery 的 boundingClientRect 一致 。 2.14.2WXS 运行在视图层(Webview) ,里面的逻辑毕竟能做的事件比较少 ,需要有一个机制和逻辑层(App Service)开发者的代码通信 ,上面的 callMethod 是 WXS 里面调用逻辑层(App Service)开发者的代码的方法,而 WxsPropObserver 是逻辑层(App Service)开发者的代码调用 WXS 逻辑的机制。
🌟使用方法
WXML定义事件: <wxs module="test" src="./test.wxs"></wxs> <view change:prop="{{test.propObserver}}" prop="{{propValue}}" bindtouchmove="{{test.touchmove}}" class="movable"></view>上面的change:prop(属性前面带change:前缀)是在 prop 属性被设置的时候触发 WXS 函数 ,值必须用{{}}括起来 。类似 Component 定义的 properties 里面的 observer 属性 ,在setData({propValue: newValue})调用之后会触发 。
注意:WXS函数必须用{{}}括起来。当 prop 的值被设置 WXS 函数就会触发,而不只是值发生改变 ,所以在页面初始化的时候会调用一次WxsPropObserver的函数 。
WXS文件test.wxs里面定义并导出事件处理函数和属性改变触发的函数:
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) } } 目前还不支持原生组件的事件、input和textarea组件的 bindinput 事件 1.02.1901170及以后版本的开发者工具上支持交互动画 ,最低版本基础库是2.4.4 目前在WXS函数里面仅支持console.log方式打日志定位问题 ,注意连续的重复日志会被过滤掉 。🌟简易双向绑定
基础库 2.9.3 开始支持 ,低版本需做兼容处理 。
🌟双向绑定语法
在 WXML 中 ,普通的属性的绑定是单向的 。例如:
<input value="{{value}}" />如果使用 this.setData({ value: ‘leaf’ }) 来更新 value ,this.data.value 和输入框的中显示的值都会被更新为 leaf ;但如果用户修改了输入框里的值 ,却不会同时改变 this.data.value 。
如果需要在用户输入的同时改变 this.data.value ,需要借助简易双向绑定机制 。此时 ,可以在对应项目之前加入 model: 前缀:
<input model:value="{{value}}" />这样 ,如果输入框的值被改变了 , this.data.value 也会同时改变 。同时 , WXML 中所有绑定了 value 的位置也会被一同更新, 数据监听器 也会被正常触发 。
用于双向绑定的表达式有如下限制:
只能是一个单一字段的绑定 ,如 <input model:value="值为 {{value}}" /> <input model:value="{{ a + b }}" />都是非法的;
目前 ,尚不能 data 路径,如 <input model:value="{{ a.b }}" />这样的表达式目前暂不支持 。
🌟在自定义组件中传递双向绑定
双向绑定同样可以使用在自定义组件上 。如下的自定义组件:
// custom-component.js Component({ properties: { myValue: String } }) <!-- custom-component.wxml --> <input model:value="{{myValue}}" />这个自定义组件将自身的 myValue 属性双向绑定到了组件内输入框的 value 属性上 。这样 ,如果页面这样使用这个组件:
<custom-component model:my-value="{{pageValue}}" />当输入框的值变更时 ,自定义组件的 myValue 属性会同时变更 ,这样 ,页面的 this.data.pageValue 也会同时变更 ,页面 WXML 中所有绑定了 pageValue 的位置也会被一同更新 。
🌟在自定义组件中触发双向绑定更新
自定义组件还可以自己触发双向绑定更新 ,做法就是:使用 setData 设置自身的属性。例如:
// custom-component.js Component({ properties: { myValue: String }, methods: { update: function() { // 更新 myValue this.setData({ myValue: leaf }) } } })如果页面这样使用这个组件:
<custom-component model:my-value="{{pageValue}}" />当组件使用 setData 更新 myValue 时 ,页面的 this.data.pageValue 也会同时变更 ,页面 WXML 中所有绑定了 pageValue 的位置也会被一同更新 。
🌟基础组件
框架为开发者提供了一系列基础组件 ,开发者可以通过组合这些基础组件进行快速开发 。详细介绍请参考组件文档。
🌟什么是组件:
组件是视图层的基本组成单元 。 组件自带一些功能与微信风格一致的样式 。 一个组件通常包括 开始标签 和 结束标签 ,属性 用来修饰这个组件 ,内容 在两个标签之内 。 <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 中定义的事件处理函数名 Any 任意属性🌟公共属性
所有组件都有以下属性:
属性名 类型 描述 注解 id String 组件的唯一标示 保持整个页面唯一 class String 组件的样式类 在对应的 WXSS 中定义的样式类 style String 组件的内联样式 可以动态设置的内联样式 hidden Boolean 组件是否显示 所有组件默认显示 data-* Any 自定义属性 组件上触发的事件时,会发送给事件处理函数 bind* / catch* EventHandler 组件的事件 详见事件🌟特殊属性
几乎所有组件都有各自定义的属性 ,可以对该组件的功能或样式进行修饰 ,请参考各个组件的定义 。
🌟结语
这篇为小伙伴们讲解小程序的架构之【视图层】的知识;水滴石穿 ,积少成多 。各位小伙伴让我们 let’s be prepared at all times!
✨原创不易 ,还希望各位大佬支持一下! 👍 点赞 ,你的认可是我创作的动力! ⭐️ 收藏 ,你的青睐是我努力的方向! ✏️ 评论 ,你的意见是我进步的财富!
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!