快应诞生背景
微信的小程序使得不少原来须要调动APP的场景不复存在,正式因为微信小程序的冲击,3月20日,华为联手九大手机厂商,共同举办了“快应用”标准启动发布会。“快应用”是几家手机厂商基于硬件平台共同推出的新型应用生态,用户没必要下载安装,即点即用,可以享受到原生应用的性能体验。“快应用”使用前端技术栈开发与原生渲染,兼具H5页面和原生应用的双重优势。javascript
快应用使用场景
进入小米应用商店,搜索“饿了么”:html
点击“秒开”就可使用快应用了前端
在华为应用市场搜索“快应用”:java
点击查看更多:node
能够看到一些快应用app已经上线了,使用起来体验也不错,体积也至关小webpack
微信小程序VS快应用
微信小程序推出后,尽管前期受到了很多质疑,但却一直发展很是稳健。腾讯3月21日刚刚公布的2017年整年财报中披露,自2017年1月推出小程序以来,截至2018年1月已推出58万个小程序,日活跃帐户超过1.7亿个。张小龙也曾经表示,将来两年内,小程序将取代80%的应用市场。若是该目标达成,这意味着微信小程序创建起一个强大的超级生态,极大挤压了国产手机厂商应用分发和数字广告业务的成长空间。所以,国产手机厂商的“快应用”主要针对微信小程序,与后者争抢用户和流量。web
那么快应用要和微信小程序去竞争,它们各自又有什么优缺点,谁又会更占优点呢?我我的总结以后列出了以下几点:正则表达式
微信小程序shell
优势:npm
- 微信小程序已经在市场上取得了必定的规模效益,抢占了市场先机
- 微信自带用户流量,用户黏度较高,经过微信打开小程序很是方便
- 微信小程序支持iOS和安卓两大操做系统,覆盖了全部的用户群体,不一样操做系统用户之间数据共享方便,好比iOS用户能够经过一个微信连接响安卓用户发送一个微信小程序连接
缺点:
- 微信小程序基于系统上层进行的封装,在性能上和原生app差距较大
快应用
优势:
- 基于系统层开发,将脚本转化为原生组件,运行效率更接近原声app,用户体验会更加好
缺点:
- 只支持安卓系统,在用户群体上受到限制,也没法在安卓和iOS系统之间作到数据共享
- 几大厂商合做,可能会产生分歧,如何去协调各大厂商,以及后期的利益分配等是一个大问题
- 缺少用户粘性和使用场景,若是每次打开其余应用都要去应用商店搜索快应用app的话显然不如微信小程序方便
总结:
从快应用的诞生,咱们可以看到国内手机硬件厂商开始反思本身在安卓生态中的地位,寻求转型和突破以争取更大的话语权和利益。这个方向显然是对的,可是对比微信小程序,较好的性能几乎成了快应用的惟一优点,可是随着如今手机性能不断加强以及微信小程序的不断优化,这个问题将变得忽略不计,何况如今不少微信小程序用户流畅度意境作的至关不错了,而在用户群体、使用方便性等几大方面,微信小程序占据着绝对优点,并且快应用因为自生的缺陷没法弥补这几方面的劣势,因此说快应用几乎没法撼动微信小程序的地位,更不用说战胜小程序,不过若是快应用可以在应用分发市场上对微信造成必定威胁而且从中分得一杯羹的话,那也可以证实快应用取得了成功。
环境配置
6.0版本以上NodeJS,官方推荐 v6.11.3 LTS
安装hap-toolkit:
npm install -g hap-toolkit(帮助开发者经过命令行工具来完成工程的建立等工做),在命令行中执行hap -V
会输出版本信息表示hap-toolkit
安装成功,以下命令所示:
hap -V
建立项目
建好环境后,开发者就能够利用全局hap
命令建立一个项目模板,以下所示,其中<ProjectName>
为自定义的项目名称
hap init <ProjectName>
命令执行后,会在当前目录下建立<ProjectName>
文件夹,并做为项目根目录
这个项目已经包含了项目配置与简单页面的初始代码,项目根目录结构以下:
├── node_modules ├── sign rpk包签名模块 │ └── debug 调试环境 │ ├── certificate.pem 证书文件 │ └── private.pem 私钥文件 ├── src │ ├── Common 公用的资源文件和组件文件 │ │ └── logo.png manifest.json中配置的icon │ ├── Demo 页面目录 │ | └── index.ux 页面文件,文件名没必要与父文件夹相同 │ ├── app.ux APP文件(用于包括公用资源) │ └── manifest.json 项目配置文件(如:应用描述、接口申明、页面路由等) └── package.json 定义项目须要的各类模块及配置信息,npm install根据这个配置文件,自动下载所需的运行和开发环境
目录的简要说明以下:
- src:项目源文件夹
- node_modules:项目的依赖类库
- sign:签名模块,当前仅有
debug
签名,若是内测上线,请添加release
文件夹,增长线上签名;签名生成方法请参考文档:编译工具
的openssl
编译项目
安装npm依赖
在项目根目录下,运行以下命令安装依赖包(webpack,babel等)
npm install
编译项目
在项目的根目录下,运行以下命令进行编译打包,生成rpk包
npm run build
编译打包成功后,项目根目录下会生成文件夹:build、dist
- build:临时产出,包含编译后的页面js,图片等
- dist:最终产出,包含rpk文件。实际上是将build目录下的资源打包压缩为一个文件,后缀名为
rpk
,这个rpk
文件就是项目编译后的最终产出
自动从新编译
若是但愿每次修改源代码文件后,都自动从新编译项目,请使用以下命令:
npm run watch
手机安装调试器
调试器APK是一个Android应用程序,请从站点地址下载
说明以下:
- 扫码安装:配置HTTP服务器地址,下载rpk包,并唤起平台运行rpk包
- 本地安装:选择手机文件系统中的rpk包,并唤起平台运行rpk包
- 在线更新:从新发送HTTP请求,更新rpk包,并唤起平台运行rpk包
- 开始调试:唤起平台运行rpk包,并启动远程调试工具
注意:若没法正常使用调试器,请升级手机系统到最新版本或安装平台预览版
手机安装平台预览版
较新的系统版本中内置平台正式版,即真实的运行环境。然而,更新平台正式版的时间周期较长,开发调试平台新功能可以使用平台预览版
平台预览版存在如下优缺点:
- 优势:迭代速度快,可当即体验平台新功能
- 缺点:实现与真实的运行环境存在差别,对厂商服务和第三方服务的支持存在缺陷
平台预览版APK是一个Android应用程序,请从站点地址下载
下载安装成功后,在调试器中点击切换运行平台至...mockup
便可在平台预览版上运行rpk包
在平台上运行rpk包
在调试器中唤起平台打开rpk包有多种途径,如下二者选其一便可,推荐第一种途径:
- HTTP请求:开发者启动HTTP服务器,打开调试器,点击
扫码安装
配置HTTP服务器地址,下载rpk包,并唤起平台运行rpk包 - 本地安装:开发者将rpk包拷贝到手机文件系统,打开调试器,点击
本地安装
选择rpk包,并唤起平台运行rpk包
1. HTTP请求
启动HTTP服务器
在终端中新建一个窗口,进入项目的根目录运行以下命令,启动本地服务器(默认端口为12306)
npm run server
自定义端口(如:8080)
npm run server -- --port 8080
在手机上预览运行效果
配置HTTP服务器地址有两种方式,如下二者选其一便可:
打开调试器 --> 点击"扫码安装"
,扫描终端窗口中的二维码便可完成配置(若扫描不成功,可在浏览器中打开页面:http://localhost:<your port>
,扫描页面中的二维码)打开调试器 --> 点击右上角menu --> 设置
,输入终端窗口中提示的HTTP服务器地址
配置完成后,若没有自动唤起平台运行rpk包,点击在线更新
唤起平台运行rpk包
若提示安装失败
,请检查执行npm run server的终端窗口是否正常运行
2. 本地安装
复制rpk包到手机中
将<ProjectName>/dist
目录下编译产出的rpk
包经过USB数据线或其余方式,复制到手机文件系统中
本地安装rpk包
打开调试器 --> 点击"本地安装"
,选择手机文件系统中的rpk包,并自动唤起平台运行rpk包,查看效果
配置应用基本信息
每一个应用都要有专属的名称,图标等,这些信息都须要在manifest.json
文件中配置;详细信息请参考文档:manifest文件
应用包名(package)
应用包名,是区别于其余应用的惟一标识
推荐采用com.company.module的格式,示例以下:
{
"package": "com.example.demo" }
应用名称(name)
应用名称,6个汉字之内,与应用商店保存的名称一致;框架提供保存到桌面的功能,桌面上显示的应用名即为此属性
示例以下:
{
"name": "发票小助手" }
应用图标(icon)
规则为正方形(不能是圆角),且务必无白边
{
"icon": "/Common/logo.png" }
注意:
请使用绝对路径,其中/
对应于路径<ProjectName>/src/
应用版本名称、版本号(versionName、versionCode)
应用版本名称、版本号为开发者的应用包维护的版本信息
应用版本名称为主版本.次版本
格式
应用版本号为整数,从1
开始,每次更新上架请自增1
示例以下:
{
"versionName": "1.0", "versionCode": 1 }
支持的最小平台版本号(minPlatformVersion)
支持的最小平台版本号为必填项,默认值为1000,标识开发者的rpk包兼容支持的最小运行平台版本
当使用了1000以上的平台版本新增特性时,就必须确保minPlatformVersion
最低为该平台版本号,避免上线后在更低版本平台上运行出错
示例以下:
{
"minPlatformVersion": "1000" }
配置接口列表(features)
在使用接口时,须要先在manifest中声明接口。在每一个接口文档的顶部,都附有声明接口的配置代码
以fetch网络请求为例,示例以下:
{
"features": [ { "name": "system.fetch" } ] }
配置页面路由(router)
路由,用于定义页面的实际地址、跳转地址。若是ux页面没有配置路由,则不参与项目编译。一个目录下最多只能存在一个主页面文件(不包括组件文件)
首页名称(router.entry)
首页,即应用平台启动时默认打开的页面。首页需配置为应用中某页面的名称,即在<ProjectName>/src
目录下,页面目录的相对路径
示例以下:
假设工程根目录以下所示
└── src └── Demo 页面目录,存放各自页面私有的资源文件和组件文件 └── index.ux 页面文件,文件名没必要与父文件夹相同(推荐index.ux)
假设首页为Demo目录下的index.ux文件,则首页对应的页面名称为Demo
{
"router": { "entry": "Demo" } }
页面路由对象(router.pages)
页面路由对象,key为页面名称(<ProjectName>/src
目录下,页面目录的相对路径),value为页面具体路由配置,key不要重复
页面具体路由配置(router.pages的value)包括如下属性:
- component:页面对应的ux文件名
- path:页面路径,不填则默认为页面名称(
<ProjectName>/src
目录下,页面目录的相对路径)
示例以下:
假设工程根目录以下所示
└── src |── Demo 页面目录,存放各自页面私有的资源文件和组件文件 | └── index.ux 页面文件,文件名没必要与父文件夹相同(推荐index.ux) └── Doc └── Layout 页面目录,存放各自页面私有的资源文件和组件文件 └── index.ux 页面文件,文件名没必要与父文件夹相同(推荐index.ux)
当页面名称(router.pages的key)为Demo
时,对应的页面配置(router.pages的value)包括:
- component:页面对应的ux文件名
index
- path:页面路径,默认为页面名称
Demo
{
"router": { "pages": { "Demo": { "component": "index" }, "Doc/Layout": { "component": "index" } } } }
如今,开发者就能够经过/Demo
访问到Demo目录下的index.ux页面了
配置页面UI显示(display)
UI显示,用于定义与UI显示相关的配置。支持定义:页面公用的默认UI显示、页面私有的UI显示
页面公用的默认UI显示
页面公用的默认UI显示,即被全部页面共享
以标题栏文字的配置为例:
{
"display": { "titleBarText": "页面公用的默认标题" } }
未配置私有标题的页面,标题栏文字均将显示为页面公用的默认标题
页面私有的UI显示
页面私有的UI显示,在display.pages
对象下配置:key为页面名称(与路由中的页面名称保持一致),value为页面私有的UI显示
以标题栏文字的配置为例:
{
"display": { "pages": { "Demo": { "titleBarText": "Demo页面的标题" } } } }
manifest文件
manifest.json文件中包含了应用描述、接口声明、页面路由信息
manifest
属性
|
类型
|
默认值
|
必填
|
描述
|
---|---|---|---|---|
package | String | - | 是 | 应用包名,确认与原生应用的包名不一致,推荐采用com.company.module的格式,如:com.example.demo |
name | String | - | 是 | 应用名称,6个汉字之内,与应用商店保存的名称一致,用于在桌面图标、弹窗等处显示应用名称 |
icon | String | - | 是 | 应用图标,提供192x192大小的便可 |
versionName | String | - | 否 | 应用版本名称,如:"1.0" |
versionCode | Integer | - | 是 | 应用版本号,从1 自增,推荐每次从新上传包时versionCode +1 |
minPlatformVersion | Integer | 1000 | 是 | 支持的最小平台版本号,原理同Android API Level,兼容性检查,避免上线后在低版本平台运行并致使不兼容 |
features | Array | - | 否 | 接口列表,绝大部分接口都须要在这里声明,不然不能调用,详见每一个接口的文档说明 |
config | Object | - | 是 | 系统配置信息,详见下面说明 |
router | Object | - | 是 | 路由信息,详见下面说明 |
display | Object | - | 否 | UI显示相关配置,详见下面说明 |
config
用于定义系统配置和全局数据。
属性
|
类型
|
默认值
|
描述
|
---|---|---|---|
logLevel | String | log | 打印日志等级,分为off,error,warn,info,log,debug |
designWidth | Integer | 750 | 页面设计基准宽度,根据实际设备宽度来缩放元素大小 |
data | Object | - | 全局数据对象,属性名不能以$或_开头,在页面中可经过this进行访问;若是全局数据属性与页面中data属性重名,则页面初始化时,全局数据会覆盖页面中对应的属性值 |
router
用于定义页面的组成和相关配置信息,若是页面没有配置路由信息,则在编译打包时跳过。
属性
|
类型
|
默认值
|
描述
|
---|---|---|---|
entry | String | - | 首页名称 |
pages | Object | - | 页面配置列表,key值为页面名称(对应页面目录名,例如Hello对应'Hello'目录),value为页面详细配置page,详见下面说明 |
router.page
用于定义单个页面路由信息。
属性
|
类型
|
默认值
|
必填
|
描述
|
---|---|---|---|---|
component | String | - | 是 | 页面对应的组件名,与ux文件名保持一致,例如'hello' 对应 'hello.ux' |
path | String | /<页面名称> | 否 | 页面路径,例如“/user”,不填则默认为/<页面名称>。 path必须惟一,不能和其余page的path相同。 下面page的path由于缺失,会被设置为“/Index”: "Index": {"component": "index"} |
filter | Object | - | 否 | 声明页面能够处理某种请求 |
router.page.filter
声明页面能够处理某种请求,页面能够从$page获取打开页面的参数,参见script脚本。filter的结构以下:
"filter": { "<action>": { "uri": "<pattern>" } }
属性
|
类型
|
默认值
|
必填
|
描述
|
---|---|---|---|---|
action | String | - | 是 | 请求的动做,目前仅支持view这一种 |
uri | Pattern | - | 是 | 请求的数据的匹配规则。必须是正则表达式。如https?://.* 能够匹配全部http和https类型的网址 |
能够处理全部http和https请求的filter定义以下:
"filter": { "view": { "uri": "https?://.*" } }
display
用于定义与UI显示相关的配置。
属性
|
类型
|
默认值
|
描述
|
---|---|---|---|
backgroundColor | String | #ffffff | 窗口背景颜色 |
fullScreen | Boolean | false | 是不是全屏模式,默认不会同时做用于titleBar,titleBar须要继续经过titleBar控制 |
titleBar | Boolean | true | 是否显示titleBar |
titleBarBackgroundColor | String | - | 标题栏背景色 |
titleBarTextColor | String | - | 标题栏文字颜色 |
titleBarText | String | - | 标题栏文字(也可经过页面跳转传递参数(titleBarText)设置) |
menu | Boolean | false | 是否显示标题栏右上角菜单按钮 |
pages | Object | - | 各个页面的显示样式,key为页面名(与路由中的页面名保持一致),value为窗口显示样式,页面样式覆盖default样式。 |
示例:
{
"package": "com.company.unit", "name": "appName", "icon": "/Common/icon.png", "versionName": "1.0", "versionCode": 1, "minPlatformVersion": 1000, "features": [ { "name": "system.network" } ], "permissions": [ { "origin": "*" } ], "config": { "logLevel": "off" }, "router": { "entry": "Hello", "pages": { "Hello": { "component": "hello", "path": "/", "filter": { "view": { "uri": "https?://.*" } } } } }, "display": { "backgroundColor": "#ffffff", "fullScreen": false, "titleBar": true, "titleBarBackgroundColor": "#000000", "titleBarTextColor": "#fffff", "pages": { "Hello": { "backgroundColor": "#eeeeee", "fullScreen": true, "titleBarBackgroundColor": "#0000ff", "titleBarText": "Hello" } } } }
源码文件
APP,页面和自定义组件均经过ux文件编写,ux文件由template模板、style样式和script脚本3个部分组成
app.ux
当前app.ux
编译后会包含manifest配置信息
(能够在npm run build
以后查看文件内容),因此请不要删除/**manifest**/
的注释内容标识。
您能够在<script>
中引入一些公共的脚本,并暴露在当前app的对象上,以下所示,而后就能够在页面ux文件的ViewModel中,经过this.$app.util
访问
<script> import util from './util.js'
module.exports = { /**manifest**/, util: util }
</script>
页面路由
导入模块 import router from '@system.router' 或 var router = require("@system.router")
接口定义
router.push(OBJECT)
跳转到应用内的某个页面
参数:
参数
|
类型
|
必填
|
说明
|
---|---|---|---|
uri | String | 是 | 要跳转到的uri,能够是下面的格式:
|
params | Object | 否 | 跳转时须要传递的数据,参数能够在页面中经过this.param1 的方式使用,param1为json中的参数名,param1对应的值会统一转换为String类型 |
示例:
// launch phone app router.push({ uri: 'tel:10086' }); // open page by path router.push({ uri: '/about', params: {testId:'1'} }); // open page by name router.push({ uri: 'About', params: {testId:'1'} }); // open web page router.push({ uri: 'http://www.example.com' }); // install apk router.push({ uri: 'internal://cache/example.apk' });
router.replace(OBJECT)
跳转到应用内的某个页面,当前页面没法返回
参数:
参数
|
类型
|
必填
|
说明
|
---|---|---|---|
uri | String | 是 | 要跳转到的uri,能够是下面的格式:
|
params | Object | 否 | 跳转时须要传递的数据,参数能够在页面中经过this.param1 的方式使用,param1为json中的参数名,param1对应的值会统一转换为String类型 |
示例:
router.replace({
uri: '/test' params: {testId:'1'} })
router.back()
返回上一页面
参数:
无
示例:
// A页面 router.push({ uri: 'B' }) // B页面 router.push({ uri: 'C' }) // C页面经过back,将返回B页面 router.back(); // B页面经过back,将返回A页面 router.back();
router.clear()
清空全部历史页面记录,仅保留当前页面
参数:
无
示例:
router.clear()
router.getLength()
获取当前页面栈的页面数量
返回值:
类型
|
说明
|
---|---|
Number | 页面数量 |
示例:
var length= router.getLength() console.log("pages' length = "length);
router.getState()
获取当前页面状态
返回参数:
参数名
|
类型
|
说明
|
---|---|---|
index | Number | 当前页面在页面栈中的位置 |
name | String | 当前页面的名称 |
path | String | 当前页面的路径 |
示例:
var page = router.getState() console.log("page index = "+page.index); console.log("page name = "+page.name); console.log("page path = "+page.path);
快应用技术架构
快应用经过脚原本编写组件,安卓内部嵌入一个脚本解析引擎,将脚本转化为原生控件,经过编译生成rpk文件,应用调试器联系起脚本及安卓系统进行调试工做,具体流程图以下:
参考资料
- 开应用开发文档:https://doc.quickapp.cn