作者说:
饿了么是一年前开始参与内测尝试开发快应用的,看着快应用平台一步一步的走过来,发展的越来越好。目前来说快应用开发条件已经比较完善,本次分享,为大家介绍前端开发人员眼中快应用的开发与使用 Vue.js 开发的一些异同,以及通过实践积累出的一些方法的正确使用姿势。
面向的场景
目前快应用在饿了么中的用户场景为:
用户在「应用商店」、「浏览器」、「自带搜索助手」搜索饿了么,出现快应用的条目,用户点击饿了么快应用,无需安装,直接打开快应用。同时会提示用户是否生成桌面图标,并且用户可以收到来自服务进行的推送,体验和原生 App 相差无异。
开发工具与条件
与前端的对比
页面布局
快应用整体基于 Fex 布局:
页面标签
如果要将原生 HTML 布局迁移至快应用,需要注意的是快应用中没有 H1~H6 标签,同时一些 HTML5 标签例如<header>
<footer>
<article>
等都要做改动。
另外文本不能单纯放在 <div>
中,只能放在 <a>
<span>
<text>
标签中。
使用时要注意好嵌套关系,比如 <a>
标签的子组件只支持 <span>
标签,<text>
标签的子组件只支持 <a><span>
。
<img>
图片标签在快应用中用 <image>
标签替代。
<rating>
是原生 HTML 没有的,是用来显示星级的组件,显示星级在饿了么 App 中处处可见,这个功能非常实用,省去了我们手写去实现的时间。
Storage
快应用中打包后是一个 .rpk 程序,因此并不需要 sessionStorage。在快应用中,若想要做本地存储,可以直接使用 Storage 方法:
例如读取存储的用户信息,见下面代码。
storage.js:
import storage from '@system.storage'const User = () => { return new Promise((resolve, reject) => {
storage.get({
key: 'USER',
success: (currentUser) => {
currentUser = currentUser && JSON.parse(currentUser) if (!currentUser) {
currentUser = undefined
}
resolve(currentUser)
},
fail: (err) => {
reject(err)
}
})
})
}export default {
User
}
Home.ux:
import { User } from '../Common/service/storage'User().then((currentUser) => { this.currentUser = currentUser; this.loadData()
})
通过 storage.get、storage.set、storage.clear、storage.delete 可以随心所欲的操作存储我们的数据了。
与 Vue.js 的对比
使用过 Vue.js 的同学看了快应用的官方文档后会发现快应用的的 API 大量的借鉴了 Vue.js,甚至一些方法名也是一样的。虽然如此,但是二者一个是基于手机平台的新型应用状态,一个是构建用户界面的 Web 应用框架,在实际开发中还是有很多不一样的地方,下面简单介绍总结一下两者的异同。
开发环境及工具
快应用的开发环境对 NodeJS 的版本要求很严格,官方推荐安装 v6.11.3, 如果开发者本地的 NodeJS 版本过高,快应用的 Toolkit 工具安装时会报错,官方给的解释是:内部 ZipStream 实现与 node-archive 包不兼容。安装完 Toolkit 之后,也可以像 Vue.js 一样通过脚手架工具快速生成一个项目模板:
hap init <ProjectName>
除此之外,快应用还需要在手机上安装调试器和平台预览版,用来在开发过程中实时预览界面和开发调试平台新功能,最后可以按照官方的文档跑起这个文档项目。本地开发快应用推荐使用 VSCode,因为可以在 VScode 里下载官方推荐的 Hap Extension 插件。
而对于 Vue.js 而言,所有支持 Language Server Protocol 的编辑器都适合 Vue.js。因此主流的前端编辑器都支持 Vue.js 语法开发。
相对比而言,Vue.js 的对开发环境和开发工具的限制更小一些。但是一个是新起的快应用,另一个是有四年时间积累的 Vue.js ,自然不能这么去对比,快应用的开发条件的完善目前只是时间问题。
模板
快应用和 Vue.js 都是使用 <template>
标签来定义模板,小程序也是如此。但是 Vue.js 中的模板的定义类型会更多一些。
快应用中在模板中直接通过 import 进行引入,例如:
<import name='comp' src='./comp'></import><template>
<div>
<comp prop1='xxxx' onevent1="bindParentVmMethod1" @event-type1="bindParentVmMethod1"></comp>
</div></template>
它也可以通过 (on|@) 语法绑定自定义子组件上的事件,Vue.js 是将自定义组件注册到 components 中再去引用。
生命周期
快应用的常用的生命周期如下:
生命周期 | 描述 |
---|---|
onInit | 可以开始使用页面中的数据 |
onReady | 开始获取DOM节点(如:this.$element(idxxx) ) |
onShow | 页面被切换重新显示时会被调用 onShow |
onHide | 页面被切换隐藏时调用 onHide |
onDestroy | 页面被销毁时调用 |
通过描述,我们可以用实践总结出一些这些生命周期实际的使用场景。
页面后退时触发,数据需重置,例如用户进入饿了么首页 -> 点击左上角进行修改地址 -> 进入地址页面 -> 选择地址 -> 自动返回到首页 -> onShow() 事件监听 -> 更新左上角的地址。
场景与 onShow 相反
该页面某个行为使用了 setInterval() 方法,离开该页面时在 onDestroy() 中 clearInterval 保证下次进入时仍是初始化的状态。
在 Vue 中生命周期除了上面的 onShow 和 onHide,其他差别不大,对于 Vue.js 中 onShow 可以用 watch 去监听路由变化来上述 onShow 中的场景,例如:
watch: { // 如果路由有变化,会再次执行该方法
$route: 'initPageData'
}, methods: {
initPageData () { // do something
}
}
数据绑定
内容绑定,两者采用的都是 Mustache 语法(双大括号),两者都可以在内容中插入 HTML。
快应用:
<text>{{ title }}</text><richtext>{{ message }}</text>
Vue.js:
<div>{{ title }}</div><div v-html="message">
条件与列表渲染
快应用的条件渲染有 if/elif/else 这3个相关指令,用于控制是否增加或者删除组件:
<text if="{{conditionVar === 1}}">if: if条件</text>
<text elif="{{conditionVar === 2}}">elif: elif条件</text>
<text else>else: 其余</text>
同时还有 show 指令,用于控制组件的显示状态,并不会从 DOM 结构中删除:
<text show="{{showVar}}">show: 渲染但控制是否显示</text>
列表渲染:
<div class="city" for="city in cities">
<text>城市:{{city.name}}</text></div>
Vue.js 的条件渲染:
<div v-if="type === 'A'">
A</div><div v-else-if="type === 'B'">
B</div><div v-else-if="type === 'C'">
C</div><div v-else>
Not A/B/C</div>
show 指令:
<h1 v-show="ok">Hello!</h1>
列表渲染:
<li v-for="todo in todos">
{{ todo }}</li>
两者从使用逻辑上来说几乎没有什么区别,只是写法略有不同,但是快应用中增加了一个组件 <block>
,可以使用 <block>
实现更为灵活的“列表/条件渲染”。
<block>
是一个仅表达逻辑的组件,并不会在页面渲染中生成 DOM 节点,这个可以说是很得人心。Vue.js 里面也有一些内置的不会产生 DOM 节点的组件,例如 <component>
、<keep-alive>
、<transition>
、<transition-group>
、<slot>
。简单举个例子, 使用 Vue 保留的<component> 元素,将多个组件动态地绑定到 <component>
元素的 is
属性上。
<template><component :is="isSignedIn ? 'home' : 'welcome'"></component></template>
<script>import Vue from 'vue'import Home from './home.vue'import Welcome from './welcome.vue'export default Vue.extend({ components: {
Home,
Welcome
}
})
</script>
在 Vue.js 中只有上述几个内置组件,使用时组件自身不产生 DOM 节点,在除了这几种内置组件之外的需求我们只能在循环块的外面加一个 <div>
去用 v-if 来判断循环块的显示隐藏,但是有时候父 <div>
可能会对内部块的样式带来不好的影响,我们还要在父 <div>
加上一些样式来消除该影响,同时也让 DOM 数层级会变得沉重冗余。但是有了 <block>
组件就很开心了,需要隐藏这个块,直接在外围加 <block>
就好了,渲染出来的 DOM 清爽干净。
属性的获取
快应用中也有 event,可以通过点击事件来传入相应的函数,通过打印 log,可以看一下具体包含什么:
<input id="test-link" class="link" data-detail="点击这里" @click="handleClick" type="button">去点餐</button>
打印出来的信息:
{ "type": "click",
"target": { "ref": "5",
"type": "input",
"attr": { "type": "button",
"dataDetail": "点击这里",
"id": "test-link"
},
"style": { },
"event": [ "click"
]
},
"timestamp": 1522837358823}
通过 event 我们可以发现 target.attr.id 就是该元素的 id,在 event 中我们还可以看到定义的 data-detail 在 target.attr 中出现了。可见快应用中也可以自定义元素属性参数值。这样子就可以利用这个 target.attr 做一些事情了,比如我们想获取这个按钮上的文本,可以在 input 标签上加入 data-name="去点餐"
,那么就可以将其绑定到 attr 中,我们可以通过 event.target.attr.dataName 去获得这个按钮的名字。但是这并不是被快应用中所推荐,这样使用,控制台会有报错提醒:
ERROR: 组件 `input` 不支持属性 `data-detail`,
支持的属性有 [type, checked, name, value, placeholder, id, style, class, disabled, if, elif, else, for, tid, show]
Vue.js 支持通过 ref 属性来访问其它组件和 HTML 元素。并通过 this.$refs
可以得到组件或 HTML 元素的上下文。在大多数情况下,通过 this.$refs
来访问其它组件的上下文同样也是不被 Vue.js 所推荐。
事件绑定
快应用中支持的事件有:
名称 | 参数 | 描述 |
---|---|---|
click | - | 组件被点击时触发 |
longpress | - | 组件被长按时触发 |
blur | - | 组件获得焦点时触发 |
appear | - | 组件出现时触发 |
disappear | - | 组件消失时触发 |
swipe 正式版本可用 | 组件上快速滑动后触发 |
使用的时候使用 on 或者简写 @ 绑定事件,例如 :
<a @click="handleClick">开始</a>
这种方式与 Vue.js 也很相似,不过 Vue.js 额外提供了事件修饰符,可以阻止一些事件的传播等,例如:
<!-- 阻止单击事件继续传播 --><a @click.stop="handleClick">开始</a>
事件监听
快应用的事件监听与 Vue.js 相似,都是使用 $on()
、$off
$emit()
去监听,移除,触发事件,但是还是有一些区别。
监听快应用的原生组件事件就用到了我们上面所说的提过的 event 的 target 来获取当前组件的信息, 例如用户选择取餐人的性别功能: