作者:饿了么 顾诚
开篇也不扯那些没用的,让我们高效的开始教程。
在一切教程开始前
无论学习什么语言、框架、库,首先你要做的,就是登陆它的官网,认真阅读它的入门手册、文档等,这比任何《xx 分钟从入门到精通》、《xx 深入浅出》都要靠谱得多,再加上现在编程体系的愈加完善,开发者越来越注重基础设施的搭建,官网绝对是学习一样东西的首先选择。 所以,快应用现在的 官网是:https://www.quickapp.cn 文档是:https://doc.quickapp.cn 请认真阅读文档。
在快应用的教程开始之前
快应用宣传的一大优势,就是对于 Web 前端开发者的低学习成本、快速上手。那么它的这个优势,其实是建立在你熟悉(有一定程度了解)类 Vuejs 的框架的前提上的。 它基本上是建立在 Vue 的体系上的,经过一定程度的改造、精简,形成了快应用的开发体系。 本篇教程,也默认你对 Vue 已经有一定程度的了解,假如你还没有完成这个前提,那么先去了解一下 Vue 也不错。 所以,让我假设阅读本文的你,是一名拥有 Vue 经验的 Web 前端开发者,对新出的快应用有一定兴趣,想要了解一下它,那么,希望文章对你有所帮助。
教程前置准备
上文也说了,快应用的开发,对比的就是 Vue 的体系,所以,我选择通过对比同样实现目标的 APP 的 Vue 版本和快应用版本,来说明你要进行快应用开发需要准备和了解哪些东西。 首先,我选择了人民群众喜闻乐见的 HackerNews 作为实现目标,它提供了完善的 API,方便我们构建真实 APP,并且 Vue 的官方也有一个 Demo 版本的 HackerNews 源码,省去了我们不少工作。 所以我们直接选择 https://github.com/vuejs/vue-hackernews 当前 master 分支作为 Vue 版本的代码,你们也可以自行 clone 查看。
快应用项目初始化
请根据快应用官方文档的快速入门一节,准备好开发环境,在这里只有一点提醒:假如你的手机还没有直接支持快应用的运行环境(提示没有运行环境),请安装「快应用平台预览版」,通过它进行调试。
开始
首先确认一下我们的工程结构
.
├── quickapp
│ ├── README.md
│ ├── build
│ ├── dist
│ ├── node_modules
│ ├── package-lock.json
│ ├── package.json
│ ├── sign
│ └── src
└── vue
├── LICENSE
├── README.md
├── index.html
├── node_modules
├── package.json
├── src
├── static
└── webpack.config.js
复制代码
就是这样,快应用是通过 hap 工具直接初始化的工程,作为开发者,直接写代码就完事了,我想你目前应该也不关心快应用的实现原理和内部细节。
1. 第一步:入口
Vue 项目的入口自然是 index.html 和 main.js,它的核心代码结构是这样的:
src
├── components
├── filters
├── main.js
├── store
└── variables.styl
复制代码
好,看一下 main.js
import Vue from 'vue'
import Router from 'vue-router'
import { domain, fromNow } from './filters'
import App from './components/App.vue'
import NewsView from './components/NewsView.vue'
import ItemView from './components/ItemView.vue'
import UserView from './components/UserView.vue'
// install router
Vue.use(Router)
// register filters globally
Vue.filter('fromNow', fromNow)
Vue.filter('domain', domain)
// routing
var router = new Router()
router.map({
'/news/:page': {
component: NewsView
},
'/user/:id': {
component: UserView
},
'/item/:id': {
component: ItemView
}
})
router.beforeEach(function () {
window.scrollTo(0, 0)
})
router.redirect({
'*': '/news/1'
})
router.start(App, '#app')
复制代码
很简单,做了几件事情:
- 引入框架(Vue,VueRouter)
- 引入组件
- 定义 filters、路由
- 启动应用
OK,那么我们去快应用里面看一下,快应用里,一个 APP 的 入口,是 src/manifest.json
,一个 APP 的重要信息就定义在这里面,我们看一下
{
"package": "me.ele.hacknews",
"name": "quickapp-hackernews",
"versionName": "1.0.0",
"versionCode": "1",
"minPlatformVersion": "101",
"icon": "/Common/logo.png",
"features": [
{ "name": "system.prompt" },
{ "name": "system.router" },
{ "name": "system.shortcut" },
{ "name": "system.fetch" }
],
"permissions": [
{ "origin": "*" }
],
"config": {
"logLevel": "debug"
},
"router": {
"entry": "News",
"pages": {
"News": {
"component": "index"
},
"User": {
"component": "index"
},
"Item": {
"component": "index"
}
}
},
"display": {
"titleBarBackgroundColor": "#ff6600",
"titleBarTextColor": "#ffffff",
"menu": true,
"pages": {
"News": {
"titleBarText": "Hacker News | QuickApp Clone"
}
}
}
}
复制代码
当然,这和你刚初始化完成时候的配置肯定不一样了,不过我只是改成了 HackerNews 的配置,结构并没有改变,来看一下:
- 定义好包名(package)、应用名(name)、logo(logo)
- 定义需要用的系统接口(features),所以上文让你认真看文档,调用系统接口都是需要提前声明(你也不想用户说你滥用权限对吧),这里我们需要router(路由)、fetch(网络请求)
- config.logLevel 先改成 debug 好了,输出所有 log
- router 中定义好所有路由,entry 就是首页,pages 和 component 对应好
- display 你可以先不管(不过也没什么难度就是了)
那么对比 Vue 的启动配置来说,因为快应用是运行在系统集成环境中的,所以 Vue 项目要求的引入框架、库(路由)就都不需要了,多出了系统接口的提前声明,按需声明就可以了,这项目只需要最基本的路由、网络请求权限就可以了。
路由这块,请参考官方文档,稍微需要注意的就是 page 默认使用它的名称文件夹作为它的组件所在位置。基本上也和 Vue 版本的路由对应的配置就可以了,需要注意的是路由参数不需要在这里配置,而是在对应的组件内配置(作为组件外部传入属性)。
到这里,你就已经完成了快应用项目的基础配置,当然,现在是运行不起来的,你配置的页面还没有实现对应的组件,下面我们就把 Vue 的页面组件改造成快应用的组件。
2. 改造 Vue-> QuickApp
看一下快应用版本的源代码目录结构
src
├── Common
├── Item
├── News
├── User
├── app.ux
├── filters
├── manifest.json
├── store
└── util.js
复制代码
一个页面对应一个目录,app.ux 是入口组件(现在不用管它),我们开始改造过程。
第一个页面就是首页(News),上改造前后对比图,diff 图左边是 Vue 项目代码,右边是快应用代码,我们来逐一分析:
template 部分
- 自定义组件引入,用特殊的 import 标签引入,直接可以在 template 中使用
- 用 list 组件提高列表性能,就先把它当成 div 好了(想要进一步了解,请阅读官方文档,list 优化一节)
- 快应用不支持 :before, :after 伪类,因此 loading 文字只能用一个单独的元素并根据条件来展示了。
- 你也发现在快应用中:
- v-show -> show
- v-for -> for
- v-if -> if
- :property -> property="{{ }}"
- track-by -> tid
- $index -> $idx 基本上就是去掉了 Vue 特色,变成了通用单词。 另外,大部分表达式使用时(除 for、tid 等),要写在
{{ expression }}
中,需要注意一下。
- 文本写在
text
组件内!(请认真阅读文档),这和 Web 开发有很大区别,HTML 的体系中,基本是个标签就能加文本,但是快应用中不行,需要绑定在text
组件内。文本写在text
组件内!文本写在text
组件内! - 没有 filter,这个在某些情况下确实有些不方便,只能改造成函数了
$index | formatItemIndex
->formatItemIndex($idx)
。
完成以上步骤,你的 template 部分就基本改造完成了,对着手册一一改造就可以了。
script 部分
这部分改动很少,快应用支持 ES2015 的绝大部分语法、特性,这里有一个不好的地方,官方没有详细说明支持程度,可能会遇到支持性问题。
- 引入路由服务,因为需要手动跳转(快应用也支持 href 跳转,这里只是为了演示),使用系统服务,就按照正常的引入项目模块的逻辑来进行就可以了。
- 不在 script 引入自定义组件。
- 生命周期,改个名字,常用的就是 onInit、onReady、onDestroy,顾名思义,不懂看文档。
- 方法,快应用的方法不需要定义在 methods 里面,因为它的组件里,没那么多其余属性,直接在组件对象第一层下定义方法就可以了,script 和 template 通用。因此我们把所有方法都从 methods 里面搬了出来。
- filters,上文说了,快应用没有 filter,就改造成普通方法好了(坏处是不能定义全局 filter,每个组件都需要反复引入 filter 方法,当然可以在 App 层面定义方法,不过不好管理)。
- 补上一个页面跳转的方法,可以看一下使用方式,就是普通的函数调用,uri + params
完成了以上步骤,你就基本完成了script 部分的改造,这里有一个问题,Vuex 你不禁想问,Vuex 怎么办,上面我写的那个 store,是 vuex 吗?不是。 很遗憾,假如你依赖于 Vuex,你可能会遇到麻烦,我还没有很好的办法替代 Vuex,快应用支持全局 data,支持 watch,但是并不足以替代 Vuex,上面的 store,并不是 Vuex,只是一个处理数据的服务,下文会说到。
style
style 部分看上去是最简单,但其实是最麻烦的部分:
- style 仅支持部分 CSS 属性
- 快应用使用 flex 进行布局
- 快应用有默认的屏幕适配(750px),你写的所有 px 单位,都会以 750px 为标准进行缩放适配,有优势,也有缺点。
- 快应用有一些特有的属性
- 快应用的选择器支持比较有限
上面这些特点,需要你认真阅读快应用文档的 style部分,以及各个组件的样式部分,来了解情况,请一定要认真阅读,你很容易用上了不支持的 CSS 特性。
我们这个 DEMO 项目的 style 比较简单,Vue 项目用了 stylus,快应用显然不支持,为了方便转成了 CSS,不过快应用支持 Less CSS,也是不错的选择。
- 根据 flex 布局做一些调整
- 把伪类改成了独立元素
- 移除了不支持的属性
- 移除不支持的伪类(hover 之类的)
- 适配一些不支持的缩写语法
这可能是一个体验很糟糕的过程,写着名义上是 CSS 却有那么多不同的 style 代码,不过无论如何,我们总算是完成了 style 部分的改造。
改造小结
很明显,通过以上对比,你会发现,快应用的代码体系,真的很像 Vue,但是又有那么一些糟糕的区别。 还是那句话,如果你想要进行快应用的开发,请认真阅读文档,磨刀不误砍柴工。
组件之外
显然我们的项目里,一般不会只有组件这一种东西,还有一些 JavaScript 模块,比如上文的 store,这时候正文开始了 :)。
diff 图左边是 Vue 项目代码,右边是快应用代码
what? firebase 库? 讲道理,这是一个 JavaScript 库,应该可以在快应用环境中直接使用,但是实际上,不行。
firebase 库依赖于很多浏览器(window)API,比如 XHR 请求,这时候,它就无法在快应用的体系中正常运行了。 这时候你陷入了一个两难的抉择,对 firebase 的库进行魔改,或者自己造一个 firebase 库。 这时候你可能就想放弃了,事实就是这么残酷,你会想,我做了这个事情我怎么不去加入 firebase?
但是教程总是要继续,好在我们这个项目真的很简单,简单到我不需要 firebase 的库,也可以轻松兼容原代码,完成改造。
- 引入 fetch API(和浏览器中 Fetch api 有一定区别)
- 声明 hackernews 的 firebase API host 地址
- 把原先的 firebase 方法
api.child
改造成直接的 fetch 调用
看上去好像也没什么问题,因为我们这里的 firebase 调用足够简单,仅仅是请求数据而已。
- 像是 fetch 这样的快应用系统接口,都是用 callback 回调的形式实现的
- 快应用支持 Promise,因此我们可以轻松实现 Promise 兼容,避免了业务层的改动
- 快应用也支持直接引入 Node 模块
万幸,我们完成了 store 部分的改造。
教程的尾声
然后其实也只剩下 Item、User 等组件了,依次完成改造。按照官方开发环境的说明
- npm run server
- 用手机安装调试工具(可能还需要平台预览版),扫描二维码
- 查看一下效果
预览图:
预览视频:
你要问了
Q: 为什么这么丑? A: 没时间调整样式,这是个 DEMO(侧面反映,普通 CSS 不能完美地直接迁移到快应用内)
Q: 为什么页面加载这么慢? A: 因为 firebase 的接口慢,并且接口是先请求 id 列表,然后逐一请求 item,最后合并返回,必然很慢,因为我们不能使用 firebase 库。。。
Q: 你吹嘘的那些快、轻量、原生完全没体现出来! A: 确实,这个 DEMO 是在太简单了,如果你有兴趣,可以尝试自己动手写一个快应用 APP,一定可以直观地感受到它和普通网页应用的区别
Q: 我看完你的教程,觉得什么都没讲?!还是不会写! A: 在教程的一开始,我已经说过了,学习一样东西,最好的方法当然是它的官网啦,其实现在快应用的快网已经非常完善了,从入门教程到手册到性能提升建议到深入实践,看完你一定有所收获。
Talk is cheap, show me the code.