快应用入门,看这篇就够了

8,943 阅读10分钟

什么是快应用

简单地说快应用是国内的十大主流手机厂商比如小米、华为、ov 等联合推出的一种新型应用。无需安装,秒开,体验媲美原生。还提供了像原生应用一样的入口:应用商店,搜索页等。

开发前准备

接下来会教大家如何搭建、启动、预览和调试快应用项目。和官方文档类似,这里我增加了一些我在这过程中遇到的坑及解决方法。

创建项目

安装 NodeJS

官方说需安装 6.0 以上版本的 NodeJS,推荐 v6.11.3,但我本机 NodeJS 是 v9.3.0,暂时没发现异常就没切到 6.0。

安装 hap-toolkit

hap-toolkit 是快应用的开发者工具,帮助开发者通过命令行工具辅助开发工作的完成,主要包括创建模板工程,升级工程,编译,调试等功能。类似 vue-cli。

npm install -g hap-toolkit

安装之后查看下版本号看是否安装成功。

hap -V

创建项目

运行如下命令会在当前目录下创建 <ProjectName> 目录作为项目的根目录。

hap init <ProjectName>

这个项目已经包含了项目配置与示例页面的初始代码,项目根目录主要结构如下。

├── sign                      rpk 包签名模块
│   └── debug                 调试环境
│       ├── certificate.pem   证书文件
│       └── private.pem       私钥文件
├── src
│   ├── Common                公用的资源和组件文件
│   │   └── logo.png          应用图标
│   ├── Demo                  页面目录
│   |   └── index.ux          页面文件,可自定义页面名称
│   ├── app.ux                APP 文件,可引入公共脚本,暴露公共数据和方法等
│   └── manifest.json         项目配置文件,配置应用图标、页面路由等
└── package.json              定义项目需要的各种模块及配置信息

安装依赖

npm install

启动项目

编译

npm run build

编译生成的 dist 目录里才是最终产物:rpk 文件。

这一步可能会遇到报错(我就遇到了)。

Cannot find module '.../node_modules/hap-tools/webpack.config.js'

主要是因为创建项目后就有一个 node_module 文件夹了,里面有一个 hap-tools 包。如果 npm install 安装依赖,高版本的 npm 可能会把 node_module 原有的包清空再安装依赖,这时只要再手动安装下 hap-tools 就行了

npm install hap-tools

如果要监听源码变化自动编译,可以运行 watch 命令。

npm run watch

到这一步一个 hello world 的快应用就打包好了,下面需要在手机上把它跑起来。

预览

首先需要安装手机调试器

只安装这个快应用调试器会发现上面的按钮都是灰色不可点击的,这时还需要安装平台预览版调试器,总之快应用文档上的手机调试器都要安装才能调试。

安装好调试器后就把快应用安装包安装到手机上就可以了。

扫码安装

需要启动一个本地 HTTP 服务器。

npm run server

如果命令行中的二维码扫了没反应,可以把那个地址在浏览器中打开在扫码试试(我就是这样),因为命令行中的二维码可能绘制的有问题。

本地安装

rpk 文件传到手机上安装即可。

在线更新

快应用调试器右上角可以设置服务器地址,运行以下命令每次改了代码就可以点击在线更新就可以更新了,不用每次都扫码或本地安装。

npm run server
npm run watch

调试

可以手机上预览,也可以使用 chrome devtools 调试界面,还可以查看调试日志。手机上预览上面说了,其他调试按官方步骤来就好了。

可能的坑:在用chrome devtools调试的时候可能打不开调试界面,或者调试界面空白。这时需检查:

  • 在手机调试器上点击了开始调试(点了就会自动在 pc chrome 上打开 devtools)
  • 确保手机和电脑在同一个网段
  • 检查代理,设置了代理的把代理关了试试(我就是因为设置了代理 devtools 空白)

5 分钟上手教程

以一个列表页和详情页为例说明快应用的代码,数据来源迅雷影评

Manifest.json

manifest.json 中配置路由后就可以写代码了,生成的模板有例子。注意不能配置动态路由。

注意用到的系统接口要先在 manifest.jsonfeature 中声明。看 manifest 的文档了解具体的配置项。

{
  "package": "com.xunlei.movie",
  "name": "迅雷影评",
  "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" },
    { "name": "system.webview" } 
  ],
  "permissions": [
    { "origin": "*" }
  ],
  "config": {
    "logLevel": "debug",
    "designWidth": 640
  },
  "router": {
    "entry": "List",
    "pages": {
      "List": {
        "component": "index"
      },
      "Detail": {
        "component": "index"
      },
      "About": {
        "component": "index"
      }
    }
  }
}

列表

列表使用了快应用的list组件,这个组件是Native组件,对长列表滚动性能更好,list组件还有一个onscrollbottom事件,方便下拉加载。

image组件和前端的img标签类似,但是alt属性不同,alt是用来显示占位图的,只能是本地图片,在图片没加载出来前显示。

list-item组件中的type是必填的,要实现DOM片段的复用,要求相同type属性的DOM结构完全相同;所以设置相同type属性的list-item是优化列表滚动性能的关键。

<template>
  <list class="list-main" onscrollbottom="loadData">
    <list-item class="list-item" type="review" for="{{item in list}}">
      <image  class ="art-pic" 
        src="{{item.img}"
        alt="../Common/assets/img/default.png">
      </image>
      <text class="art-title">{{item.title}}</text>
    </list-item>
    <!-- 加载更多,type属性自定义命名为loadMore -->
    <list-item type="loadMore" class="load-more" show="{{hasMore}}">
      <progress type="circular" class="round"></progress>
      <text>加载更多</text>
    </list-item>
  </list>
</template>

快应用的网络请求是用fetch方法,是callback的形式,不方便调用,官方给了一个封装成promise的例子,可以用async/await的方式调用。

将封装好的fetch方法在app.ux中导出就可以全局使用了,由于我使用的接口都返回json,所以直接就在这一层解析了。实际开发时要注意JSON.parse的报错处理。

// app.ux
const natives = {
    /**
     * 网络请求
     * @param options
     * @return {Promise}
     */
    async fetch (options) {
      const p1 = new Promise((resolve, reject) => {
        options.success = function (data, code) {
          data = JSON.parse(data.data)
          resolve({ data, code })
        }
        options.fail = function (data, code) {
          reject({ data, code })
        }
        nativeFetch.fetch(options)
      })
      return p1
    }
  }
  // 注入到全局
  const hookTo = global.__proto__ || global
  hookTo.natives = natives
  
  export default {
    natives
  }

路由跳转

<template>
  <list>
     <list-item onclick="{{goDetail(item.id)}}" for="item in list"></list-item>
  </list>
</template>
<script>
import router from '@system.router'
  
export default {
  goDetail (id) {
      router.push({
        uri: '/Detail',
        params: { id }
      })
   }
}
</script>

webview

详情页只是加载了一个 webview, 用列表页传过来的 id 去请求影评详情,影评正文是存在 cdn 上的一个地址。使用 web 组件前需在 manifest.json 中声明使用 webview 接口。

<!-- Detail/indev.ux -->
<template>
  <!-- template里只能有一个根节点 -->
  <div>
    <web src="{{review.body_url}}" id="web"></web>
  </div>
</template>

<script>
  import api from '../Common/api/index.js'

  export default {
    data: {
      id: '', // 列表页传过来的id
      review: {}
    },
    onMenuPress() {
      this.$app.showMenu()
    },
    onInit () {
      this.getReview()
    },
    async getReview () {
      try {
        let data = await api.getReview(this.id)
        this.review = data.cinecism_info || {}
        this.$page.setTitleBar({ text: this.review.title })
      } catch (error) {
        console.log(error)
      }
    }
  }
</script>

与前端开发比较

快应用与前端开发的最大的区别就是 html 和 css 部分,因为快应用是用原生的方式实现的,但没有实现html的所有标签,而且与 html 相同的标签在用法上也有一些差别。

html

快应用中很多 html 都不能用,比如没有 p,h1~h2 等,因为它只是模拟了部分 html 标签,最终会转化成原生组件。

而且快应用中的组件嵌套子组件是有限制的,不是所有的组件都能嵌套子组件,如果嵌套不正确编译的时候会报错。比如下面就是不正确的写法:

<!--错误-->
<a href="">
 <image src="http://pic.com/1.jpg"></image>
</a>

文本组件

只能使用 a、span、text、label 放置文本内容

图片组件

图片组件是 image 不是 img,用法与 img 类似,只是 alt 的含义不同,在快应用中 alt 是指图片没加载出来前的占位图,只能是本地地址。

<image src="http://pic.com/1.jpg" alt="1.jpg"></image>

其他

表单组件、video 组件等与前端一致,还有一些快应用特有的组件,比如星级评分组件、进度条组件、list 组件等。

css

  • display 只能是 flex 或 none
  • position 只能是 fixed 或 none
  • 长度单位只有 px 和 %

与传统 web 页面不同,px 是相对于项目配置基准宽度的单位,已经适配了移动端屏幕,其原理类似于 rem。基准宽度可以在 mainifest.json 中配置。

javascript

基本语法都能用,ES6 也可以用,项目中已经安装了 babel 依赖。一些浏览器特有的 API 可能不同。比如数据存储用的是快应用的接口 storage。

与 Vue 比较

由于我们团队主要是用 Vue 技术栈开发,所以比较下快应用在语法上和 Vue 的共同点和差异之处。快应用看起来和 Vue 类似,其实还是有很大的差别。

  • 都有指令的概念,只是写法不同, 目前不能自定义指令
<!--左边是 vue 语法 右边是快应用语法-->
v-for => for
v-show => show
v-if => if
template => block
slot => slot
  • 快应用的路由是通过配置文件 manifest.json 配置的,在实例中的用法与 vue-router 一致
  • 都有组件概念,组件引入的方式略有不同
// vue
import child from './childComponent'
// 快应用
<import name="child" src="./childComponent"></import>
  • 事件的监听和触发与 Vue 类似,都是 $on $off $emit,监听原生组件的事件写法不同
<!--vue-->
<div v-on:click="handleClick"><div>
<div @click="handleClick"><div>
<!--快应用-->
<div onclick="{{handleClick()}}"><div>
  • 组件间通信和纯 Vue 类似,可以通过 props,也可以挂载在全局对象上
  • Vue 生态系统都不能用,比如 Vuex,目前没有插件机制

优缺点

优点

  • 提供了很多系统的功能,比如分享、通知、扫描二维码、添加图标到桌面
  • 用户体验好,无需下载,秒开,占用内存小
  • 可以关联原生应用

缺点

  • 每个平台都要注册个账号
  • 没有一个集成开发环境,调试麻烦,且 devtools 很卡
  • rpk 文件最大1M
  • 国内手机厂商推出的,自然是不支持 ios 了

总结

写 demo 的时候还是遇到了不少坑,主要是 html 和 css 部分。像我们公司前端和重构是分开的,重构只负责写 html + css,前端负责写逻辑调接口等杂七杂八的事情,快应用和小程序这种形式对重构来说很麻烦,不能写一份代码到处用了。

还有就是详情页显示影评正文的时候遇到了一个问题。我们影评的正文是存在 cdn 上的一堆 html 标签,无样式,可能有一些和快应用不兼容的标签,所以用 webview 的方式加载页面。但是不知道怎么向 webview 中注入 css ,所以页面是乱的。

总的来说,快应用这种形态对用户来说还是很好的,在下载 APP 前就可以体验到应用的一些功能。快应用的快在于它进行了很多原生的优化,也在于它小,小到用户感觉不到,这也注定它不能做的很复杂。

代码地址

github.com/greenfavo/q…

参考文档

快应用开发文档

扫一扫关注迅雷前端公众号

作者:珈蓝

校对:前端小透明