本期想要和大家分享下大众点评点餐小程序中View视图层的一些开发经验。本文部分示例来自于「大众点评点餐」小程序的菜单页面。页 ...
本期想要和大家分享下大众点评点餐小程序中View视图层的一些开发经验。本文部分示例来自于「大众点评点餐」小程序的菜单页面。
页面代码结构为:
menu
├── menu.html
├── menu.js
├── menu.json
└── menu.less
我们将要说的小程序的View视图层是由WXML(menu.html) 与 WXSS(menu.less) 两大部分组成,由视图最小单元 - 组件来进行展示。视图层将逻辑层的数据(menu.js+menu.json)反应成视图,同时将视图层中定义的事件发送给逻辑层,一图以蔽之。
WXML(WeiXin Markup Language)与HTML对应,用于描述页面的结构,可以类比React的JSX。项目中menu.html为WXML语法,一个页面的顶层是page节点。WXML中获取逻辑层定义的数据后,通过一系列自己的语法和逻辑展示出这些数据。结构上组件是其最小单元,通过以下方式动态渲染。
数据绑定是最简单的使用数据方式,语法采用Mustache的变量替换,用双大括号将变量包起来,如果组件的属性则需将数据放置于引号之中。
<view class="dish-item" data-id="{{dishId}}"><text class="name">{{dishName}}</text></view>
数据绑定还支持ES6规范的扩展运算符 “...”、解构赋值。
<template is="dishItem" data="{{...item, count, soldout: true }}"></template>
双大括号中可进行算数运算、三目运算、逻辑判断、字符串拼接等操作。
<text class="{{orderBanner.type !== 0 ? 'order-banner arrow' : 'order-banner'}}">{{orderBanner.text}}</text>
与常用模板语言将渲染内容写在 if/else 判断条件之中不一样的是,小程序的条件渲染将渲染条件直接写在渲染内容组件的 wx:if/wx:else 属性中,如果渲染组件为多个,可将多个组件放在
if/else
<text wx:if="{{item.soldOut}}" class="status-soldout">已售完</text>
<template wx:else is="numberCount" data="{{count: cartSpuCount[item.spuId]}}"></template>
<block>
<block wx:if="{{serverError}}">
<text>点小评去吃满汉全席啦~</text>
<button class="menu-btn" bindtap="requestMenu">重试</button>
</block>
列表渲染是将遍历元素作为渲染组件的wx:for属性值,与此相关的还有以下几个属性:
项目中数据较为复杂,使用测试数据举例:
<block wx:for="{{testData}}"
wx:for-item="mainitem"
wx:key="{{mainindex}}"
wx:for-index="mainindex">
<view wx:for="{{mainitem}}"
wx:for-item="subitem"
wx:key="{{subitem.id}}"
wx:for-index="subindex">
<view class="dom-item">第一层index: {{mainindex}} id: {{subitem.id}} name: {{subitem.name}}</view>
</view>
</block>
以上代码结构上分为两层:
1、第一层block循环遍历testData数组,每个遍历值变量名为mainitem;
2、第二层view循环遍历mainitem数组,每个遍历值变量名为subitem,展示第一层index,第二层id和name属性;
// 创建页面实例对象
Page({
/**
* 页面的初始数据
*/
data: {
"testData": [
[ {
"id": "1-1",
"name": "节点1 - 1"
}, {
"id": "1-2",
"name": "节点1 - 2"
}], [{
"id": "2-1",
"name": "节点2 - 1"
}, {
"id": "2-2",
"name": "节点2 - 2"
}]
]
}
})
展示结果:
开发过程中曾碰到
注意:
如将上面例子中testData换成对象类型:
// 创建页面实例对象
Page({
/**
* 页面的初始数据
*/
data: {
"testData": {
"a": [{
"id": "1-1",
"name": "节点1 - 1"
}, {
"id": "1-2",
"name": "节点1 - 2"
}],
"b": [{
"id": "2-1",
"name": "节点2 - 1"
}, {
"id": "2-2",
"name": "节点2 - 2"
}]
}
}
})
结果为:
模板类似于React中的组件component的概念,可以在模板中定义代码片段,然后在不同的地方调用,减少重复的代码。
1、定义:使用name属性,作为模板的名字,然后在<template/>内定义模板代码片段;
2、使用方式有2种:
注意:
示例(单个菜品组件):
<import src="../../components/common/dish-item.wxml" />
<template is="dishItem" data="{{...item}}"></template>
事件名称为字符串,会默认传入event参数,无法定制其他参数,所以一般将所需参数通过data-属性绑定至组件后通过e.currentTarget.dataset获取。
<view class="cart-btn" data-type="1" bindtap="redirectCart">选好了</view>
WXSS(WeiXin Style Sheet)与CSS对应,用于描述页面的样式。
定义在app.less中的样式为全局样式,作用于每一个页面;在page的wxss文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖app.less中相同的选择器,如代码结构中menu.less作用于menu.html。
注:绿色背景色行表示官方文档中没有说明,但经个人亲测后确定也支持的选择器。
目前不支持的选择器有:
建议:开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
注意: 由于数值较小时渲染时会存在四舍五入的情况,在较小屏幕上差距会很大,所以要求精确而较小的视图内容需避免使用此单位。
如下图所示菜品的减号操作图标,高度iPhone6(750)下是2px,iPhone4s(640)下直接渲染成了1px(实际比例值为1.7px),而加号按钮图标高度iPhone6(750)下是11px,iPhone4s(640)下渲染成了9px(实际比例值为9.48px),误差比例较小没有出现明显视觉问题,所以两者看起来会不协调。
如上WXML中所述,组件是视图层的基本组成单元,与HMTL中标签作用类似,基于Web Component标准,属性和内容的使用方法也和HTML标签类似,组件和属性都须小写。
如上统计,input、textarea、video、map、canvas为系统原生组件。原生组件相对来说性能和用户交互方面会有所提升。
以部分机型input元素fixed时唤起键盘被遮挡的问题举例,在某魅族机型上H5页面中父元素fixed的输入框会被遮挡:
同一机型小程序中,输入框不会被遮挡:
支持类型
共同属性
特殊属性
特殊属性是各个组件自己定义的属性,如 <icon> 组件的size属性,具体各参见官方文档各组件具体说明。
渲染机制
根据官方文档的说明:
由于内核渲染表现不一致,H5开发过程中存在X5浏览器和各类机型或系统的兼容性的部分问题小程序中依旧存在。
常见问题分类
前端常用的模板方案一般有2种:
当数据改变触发渲染层重新渲染的时候,会校正带有key的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
小程序对组件的渲染方式我们不得而知,只能对开发中碰到的一些问题来推测。结合小程序对列表渲染wx:key的解释可知其模板渲染属于第二种,数据更新时会根据key进行渲染优化。但小程序官方未提供相关接口或性能调试工具,所以项目中我们只能自己尝试不同方案然后对比渲染速度。以菜单页面为例,商户菜品数量多者成百上千,优化后的效果对比还是比较明显。
采取方案
本文时间为2017-02-24,所提小程序暂不支持属性或碰到的bug以此时间为准,后续更新或修复请查看官方文档。