本文为10月24日前端之巅社群群分享整理而成,分享人小米MIUI系统框架负责人董红光。欢迎关注前端之巅(ID:frontshow),获得最新前端技术干货!
今天想和大家分享的是小米直达服务平台,主要会围绕项目的背景和理念、平台的技术架构、以及一个简单的开发示例来展开。
直达服务想解决什么样的问题呢?我们可以先看看 PC 端用户是如何使用服务的。在 PC 端,除了游戏和部分专业软件以外,绝大多数情况,用户使用的都是浏览器,打开搜索引擎,搜索关键字,点击 URL 进入服务网站,使用过程中可能还会通过链接进入到另一个服务网站,但整个过程是非常顺畅的,不需要下载软件,服务之间的跳转连接也是无缝的。
但移动端却不是这样的,移动端服务的承载主要有两种形式,网页和应用,分别来看:
网页在移动端和 PC 端的地位有着天壤之别,原因主要有两个:
第一,移动端会用到大量的硬件能力,比如拍照、扫码、定位、指纹、蓝牙、传感器,等等,但是大部分能力都没有相应的 API 能够让网页使用,这就意味着用网页承载服务很可能并不完整;
第二,移动端的芯片能力比 PC 端还是要弱一些,但是界面的复杂性和要求却很高,再加上前端技术的灵活性和历史包袱,导致移动端网页非常容易变得卡顿、慢、内存占用高,使用体验非常糟糕,想做到流畅需要花费很高的代价,而且依然很难达到原生应用的效果。
而另一种承载形式——应用——同样有两个很严重的问题:
第一,想使用服务,必须先下载安装应用,通常有几十兆左右,可能用户在外面需要流量下载,可能网络速度比较慢需要等待 10 分钟以上,而等到下载安装完了,可能已经太晚用不上或者想不起来再用了,这中间有一个非常严重的断档等待期;
第二,应用的孤岛现象严重,即便已经安装了这些应用,你会发现,大部分应用之间依然无法无缝衔接,取而代之的是,一个应用中打开的是另一个服务的网页,而网页的体验经常比较糟糕,而且可能承载不了服务,这就意味着用户的操作流程可能会断掉,需要手动切到另一个应用继续接下来的操作,这对于用户来说是难以忍受的。
而孤岛现象的另一个重要体现在于很难做应用内搜索,大部分情况用户还是只能到各个应用中分别搜索,而不是一个地方搜索全部内容。
理想的移动端服务形态,应该能够像 PC 端浏览器一样的流畅体验,具体来说,就是能够结合移动端网页和应用的优点,既不需要下载安装,功能服务又完整,还能达到原生般的流畅,服务间还能无缝打通和互相索引。我们觉得这样的服务形态应该是未来很重要的一种形式,理应对此进行探索,所以才有了直达服务这个项目。
直达服务平台就是我们对上述理想的移动端服务形态的一种探索和尝试,初步达到了我们的预期,接下来在讲平台架构时会详细说明我们在技术上是怎么做到的。
这里再多说一点入口层面的事情,通常情况下,移动端服务的发现主要有三类,应用商店、搜索、场景化。直达服务充分发挥了直达服务自身的优势和 MIUI 的优势,深挖了搜索和场景化入口,举两个例子:第一个例子,在全局搜索或浏览器搜索中,可以输入一个特定内容,比如一本漫画的名字,那么无需安装漫画应用,即可直接打开诸如快看漫画这样的直达服务,以等同于原生应用的体验直接阅读这本漫画;第二个例子,在上面提到的传送门场景之下,发现一个特定内容,比如一个电影的名字,那么同样无需安装电影应用,即可直接打开诸如豆瓣电影这样的直达服务浏览电影评价。
这里先给大家发一段视频,让大家对直达服务有一个直观的印象:
直达服务平台上的各个服务采用前端技术栈开发,但是并不跑在浏览器或 WebView 中,它舍弃了浏览器内核渲染,转而采用 Android 的原生渲染机制,也就是说,实际上是使用前端技术栈开发了一个原生应用,无论是渲染效率,还是系统能力的 API 丰富程度,都远远超出传统网页。
同时,直达服务又不像原生应用先把完整应用下载下来才能运行,反而和网页类似,只下载当前需要的部分,由于这部分通常来说非常小,网络条件良好的情况下,一般不超过 1 秒即可打开服务。从开发效率角度看,直达服务和前端开发是比较接近的,采用了前端主流的模版 + 数据绑定的 MVVM 模式,非常容易理解和上手。
平台的技术架构是这样的:
首先,它有一个运行时环境,即 runtime,能够支撑一个直达服务的应用正常运行,这个 runtime 的架构示意图如下:
这里简单说明一下:
左侧黄色部分,我们提供了一套原生渲染引擎,支持将前端代码渲染成原生应用,同时我们也支持直接渲染在浏览器内核中,在此基础之上,我们封装了一套组件标签库,除了 HTML 中比较常用的以外,还有很多高阶语义的标签,比如列表、tab、轮播图等等,所有标签在原生渲染和浏览器内核渲染条件下都能使用
中间黄色部分,我们提供了一套桥接能力,将系统的接口能力暴露到前端,在此基础之上,我们暴露了大量的系统能力,比如文件存储、二维码扫描、设备信息等等,另外还集成了很多常见服务,比如推送、帐号、支付等等,在右侧紫色部分表示
上方蓝色部分,我们提供了一套 JavaScript 形式的开发框架,封装了底层的这些功能,并且提供了很多应用开发需要的功能,比如页面管理、页面路由、MVVM 支持,等等
有了运行时环境还不够,由于是平台,需要支持多个应用运行、分发、隔离等等,我们基于 runtime,打造了一整套完整的平台,平台的架构示意图如下:
这里简单说明一下:
横线上方是客户端,左侧是入口 app,比如全局搜索、应用商店等等,右侧是直达服务应用,中间是最核心的平台 apk,里面大体有四个部分,runtime 就是上面提到的运行时环境,除此以外还有应用的沙盒管理、分发管理、元数据管理等等功能
横线下方是服务器,包括上文提到的诸如推送、帐号、支付等常见的应用服务,以及开发者后台服务,另外就是平台自身的各种服务,比如分发、统计、应用仓库等服务
接下来我想通过一个最简单的应用——todos,来为大家展示直达服务的应用开发过程。
整个过程从环境准备到项目成型,包含 7 个部分:
环境准备
运行 HelloWorld
增加首页
添加标题
支持撰写内容
展示内容列表
调试工具
在开发之前,需要保证以下环境安装并配置好:
PC:NodeJS,版本 6.0 以上
PC:平台提供的 mix-toolkit 工具,用于编译项目
手机:Android 手机,并在手机上安装应用平台调试 apk
网络:确保在同一个网段,因为手机端会向 PC 端发送 HTTP 请求,获取编译后的文件
当开发者按照框架规范开发出应用之后,需要编译构建,得到一个后缀名为 rpk 的 zip 格式文件,该文件即可在手机的直达服务平台中运行。
大致过程如下:
新建工程
开发项目,更新项目代码
编译构建工程
PC 端启动 Server,提供 HTTP 请求服务
手机端平台请求应用文件,下载完成后,运行应用
开发者选定文件夹作为代码工程的目录,在目录的命令行中执行mix init todos
,新建 todos 项目,生成项目的基本代码。工程主要包含三个目录:
src:项目源文件夹
node_modules:项目的依赖类库
sign:签名模块,确保构建产出的内容安全
进入项目目录下,通过npm install
安装依赖,如:webpack,babel 等。
通过npm run build
编译项目,会在 dist 目录下生成 rpk 文件,该文件是一个 zip 格式的文件,用于在手机上运行。
通过npm run server
让项目目录提供 HTTP 服务,供手机发送 HTTP 请求获取该文件。
手机端打开调试 apk,界面如下图所示:
在 app 内扫描 PC 输出的二维码,点"在线更新"即可看到最初的模版页面效果:
开发者可以修改 src/Demo/index.mix 页面中的模板文本内容,然后在 PC 端编译项目,手机端更新项目,就会得到修改后的页面效果。
为了避免每次代码改动都要手动更新,开发者也可以使用npm run watch
替代npm run build
,完成实时监听代码文件的更新并自动编译。
本项目用于开发 todos 应用,最终效果如下:
功能很简单,也没有细调样式,只是作为示例展示。
我们先新增一个页面。
增加页面:新建 src/Home 文件夹,内部增加 index.mix 文件,内容如下:
可以看到,页面由 template/style/script 这 3 个根节点组件构成:
1) template:模板用于放置 DOM 树,只能有一个根节点,在里面可以使用各种组件标签,并配置数据进行绑定或者使用指令,如 for, if, show
2) style:即 CSS 样式表,用于配置页面样式,为了方便 CSS 预处理,我们也支持 less 预编译能力,在 style 节点上增加属性 lang="less"的声明即可
3) script:提供对页面的动态交互能力,如数据更新、事件响应,生命周期等
例如,绑定页面的 onInit,onReady 事件,在页面数据初始化、节点渲染完成后输出日志:
更新项目配置:修改 src/manifest.json,增加 Home 页面的路由,项目起始页修改为 Home,最终 router 配置如下:
这里需要说明几点:
1) manifest.json 为项目配置文件,其中的 router 属性用于设置应用内的页面路由
2) router.pages 对象表示应用的所有页面路由,当前我们有 Demo、Home 两个页面
3) router.entry 属性表示应用启动时默认加载的页面,我们让应用默认启动加载 Home 页面
重新加载 manifest.json 中的路由配置后,手机端即可看到效果。
接下来将为页面增加一个 todos 标题,文本居中靠上展示。
模板中增加 text 组件,并使用 div 组件的 header 类进行包围:
按照预览效果,我们将页面放在 div.page-home 节点中,页面分为上下两块,.header 用于放置标题与 input 内容。
样式中更新 text 组件的样式:
这里需要说明几点:
1) 使用 less 预编译能力,需要声明 lang="less"
2) 页面布局使用 Flex 布局模型,开发者可以灵活使用 flex-direction 等常用的 flex 属性
3) 通过组件标签的 class 属性声明样式,除了 flex 属性,开发这还可以使用常用的 line-height, color 等 CSS 属性
更新手机端即可看到效果:
接下来将为页面增加 input 组件,完成一条记录的添加操作。
增加 input 组件:
这里需要说明几点:
1) 使用 input[type=text] 组件,表示用户输入的内容,然后在该节点上监听 onchange 事件
2) 使用 input[type=button] 组件,表示用户点击添加的按钮,然后在该节点上监听 onclick 事件
更新样式:
这里需要说明几点:
1) .header 下新增.new-item 的 class 声明
2) 使用 CSS 属性 border 设置 div 组件的边框
3) 使用 width/height 属性设置节点的宽高
增加业务逻辑:
脚本导出的对象会被框架作为 ViewModel,主要用途有:
1) 提供生命周期的响应
2) 提供 props/data 属性,用于完成数据的存取能力,底层进行数据驱动能力的实现
3) 提供事件绑定,开发者可以将节点的事件绑定到 VM 对象的方法上
我们在 VM 上添加 input 更新内容的事件绑定updateValue()
,和按钮响应增加记录的事件绑定addItem()
:
这里需要说明几点:
1) ViewModel 的 data 中定义 newItemName 属性用于记录用户输入的内容
2) ViewModel 的 data 中定义 listData 属性用于记录用户的输入列表
3) ViewModel 中绑定 input[type=text] 的事件到方法updateValue()
,用于更新内容
4) ViewModel 中绑定 input[type=button] 的事件到方法addItem()
,用于将记录增加到列表中
使用 prompt 接口进行提示:
如果用户没有输入内容,那么可以使用 toast 提示用户,只有输入内容才可以新增。
这时需要在脚本中引入 prompt 接口,并调用其 showToast() 方法,由于使用接口,我们需要在 manifest.json 中声明对 prompt 接口的使用:
更新手机端即可看到效果:
接下来要将用户输入的内容列表显示出来。
模板中增加 list 组件:
这里需要说明几点:
1) 增加 list 组件,用于展示 DOM 列表,匹配的子组件内容使用 list-item 组件
2) 组件 list-item,每一条单独的记录会使用它来渲染
3) list-item 上通过 type 属性标识不同种类的 DOM 布局,会在原生端进行视图复用
4) 组件 block 是一个逻辑节点,不会被渲染,该组件上的数据绑定会根据指令进行处理并渲染内部子组件
5) 指令 for 用于迭代 listData 列表,循环输出每条记录,内部提供了$item
表示每条记录供子组件引用,$idx
表示记录索引
6) 增加 input[type=checkbox] 组件,表示是否该待办事项是否已完成,绑定方法modifyItemStatus()
7) 增加 text 组件,展示 X 删除待办事项的功能,绑定 click 事件到方法deleteItem($idx)
为 list 组件增加样式:
这里增加了.main 的 class 声明,表示列表模块的样式。
增加业务逻辑:
如上述模板中所示,我们需要增加两个事件响应:标识待办事项完成、删除待办事项。
最终代码如下:
这里需要说明几点:
1) 新增方法modifyItemStatus()
:点击列表中的 checkbox,标识该 todo 已经完成或者未完成
2) 新增方法deleteItem()
:点击列表中每行结束时的 X 标识,用于删除该条记录
更新手机端即可看到最终效果:
这里我也顺便介绍一下直达服务提供的调试工具,与传统的浏览器 JS 调试一样,直达服务的 JS 调试也是通过浏览器的调试器(比如 chrome 的 dev-tools)来完成的,不同的是直达服务是通过浏览器打开调试页面来完成的。
要开始调试,首先在 PC 端需要启动 Debug Server,可以运行命令:npm run debug
,此时会启动一个 HTTP Server,用于提供 HTML 调试页面的请求,默认地址为:"http://localhost:8081/"。
浏览器打开这个地址,可以看到二维码输出,然后在手机端 app 中扫描二维码,完成 PC 端浏览器与手机端运行平台的连接,即可在浏览器查看到当前页面的渲染效果,如下图:
开发者可以在调试窗口中看到工具提供了:Elements、Console、Sources、Network 四个面板,包含我们最常用的大部分调试功能。
1) 面板 Elements 可以看到 DOM 树结构,开发者也可以查看节点样式;,如下图:
2) 面板 Console 执行 JS 脚本,查看对象,如下图:
3) 面板 Sources 用于调试 JS 业务逻辑,可以看到渲染当前页面的 JS 脚本,如下图:
左侧列出页面 JS 文件,可以双击看到页面内容,进而在里面的 JS 注入断点,当事件发生时即可完成调试的功能:
如上图所示,在addItem()
内打断点,当手机完成点击 Add 按钮时会将 JS 线程停止到断点处,然后就可以做单步调试了。
4) 面板 Network 会记录页面所发起的所有 HTTP 请求,与传统一致,因为示例没有 HTTP 请求就不展示了。
以上就是小米直达服务平台的基本介绍,目前平台处于内测期间,接下来很快会对外开放,欢迎大家关注,有任何问题,可以随时和我联系,谢谢大家。
活动推荐:
12月8日InfoQ在北京举办ArchSummit全球架构师峰会,邀请了来自Google、阿里、京东、美团点评、饿了么等技术专家前来分享《大前端技术与管理》和《移动开发工程化实践》等相关技术,大会详情可点击阅读原文进一步了解。