本帖最后由 管理员 于 2019-4-22 17:47 编辑
大家好,本文来自快应用联盟的前端研发团队。
自去年3月,快应用联盟成立之后,已经有很多开发者使用快应用的标准 DSL(以 ux 文件后缀的项目形式)上线了对应产品。
以“旅游出行”的品类为例,就有:携程、去哪儿、高铁管家等。
今天,研发团队带来一个好消息,就是:快应用开放平台接口,可以支持第三方的 DSL 啦!
接下来分享主题:以流行的 Vue 框架为例,让快应用支持第三方 DSL 的开发能力。
那么为什么做这个事呢?主要还是为了满足前端同学的开发习惯,提升开发者的体验与效率。所以借助这种契机与接口开放的能力,快应用可以支持其他更多的 DSL。
过往回溯
近年来,前端开发在适配上迎来了巨大的变化,开发者写的代码,不仅要满足 WEB 平台、原生渲染平台(RN 开发),而且还要增加多个小程序生态支持。这给开发者提出了更高要求,从"面向模块的开发"到"面向多端适配"。
可喜的是:在这种背景下,前端圈子里逐渐衍生出新的框架,就是希望能够提供统一的 DSL,让开发者编写一套代码,完成多端自适应的运行效果。
面对市场上众多的框架,新手开发者如何粗略了解与选择呢?
当前现状
从历史发展与职责目标的角度看,当前市场上的 DSL 框架可以分为以下 4 类:
比如:React、Vue、Angular 等轻量级的数据驱动框架。
简述:主要用于浏览器的页面开发,因为语法简单、容易上手、调试方便而备受开发者的喜欢。
发展:因为拥有广泛的用户基础,逐渐发展多个子方向,如:UI 组件库方向(如:Ant Design、ElementUI),简化版方向(移动端性能好,扩展更多能力)。
职责:提供开发者钟爱的语法,解决前端项目中组件化、分层架构、工程组织、数据流等问题,让开发者以最优雅的方式管理项目。
2)平台型简述:主流的大互联网公司都会推出自有的渲染平台,从而为满足初期自身平台的渲染而提供一套自己实现的前端框架。
发展:每个平台拥有独特的语法,让开发者水土不服,针对该平台重新开发一套产品代码,学习成本较大。
职责:这类前端框架的存在主要是为了满足初版与迭代,更多服务于平台的系统能力提供,研发方向着重于技术深度的底层渲染(绘制、合成);对于前端框架而言,仅满足开发需求,期望培养开发者习惯,并引领开发潮流较难,除非这类 DSL 可以同时生成到移动端等的适配。
3)适配型
简述:介于诸多开发者对于上述平台型 DSL 不适应,从而引入前端受欢迎的 WEB 型 DSL。
发展:这里的发展思路差异比较大,有的是平台自身开发支持的,有的是通过 DSL 爱好者移植适配完成的,经历二次编译(WEB 型 DSL 先转成平台型 DSL,然后平台型 DSL 再转换成可以直接运行的平台编译代码)。
二次编译的优势在于:它不需要了解平台内部是如何实现的,仅需要根据官方提供的 DSL 能力进行能力适配即可;缺点在于:比较依赖平台型 DSL 能力,如果不支持某个特性则适配困难,或者容易造成性能瓶颈。
如果是平台自身支持的,那么开发者代码,直接就可以完成对 UI 的操作,跳过官方标准 DSL 的模型,减少中间调用,完成加速渲染;这种方式的难点在于:平台自身需要提供稳定的 UI 操作接口,做到向后兼容。毫无疑问,平台自身的支持,能够比二次转换,带来更好的效果;
职责:尽管各自思路不同,但是目标一致,完成开发者从 WEB 到具体平台的顺利过渡。
4)全能型
比如:滴滴的chameleon、去哪儿的nanachi、京东的taro等。
简述:该类型从上面的适配型开始萌芽,围绕如何解决多端适配问题,但这仅仅只是表象问题;对于后续壮大发展,需要思考面更广,对抽象概念理解更深刻,如:APP容器管理,渲染设计,系统功能调用,动态加载等概念。
发展:尽量抹平WEB、原生、快应用等渲染的差异,抽象应用模型,完成页面渲染设计,最后适配到各平台;当然适配时如果能够直接完成对平台API的操作,要比二次转换效果要好的多。
职责:完成较全面的多端适配,达到一套代码多端适配的目标。
那么本文讲述的快应用引入Vue DSL,属于上述的适配型,让平台自身支持,同时开发平台接口,为往后出现的全能高效型框架服务。
近期趋势
在作者看来,新的 19 年,全能型框架会逐渐取代适配型,并且从规范、架构、设计等角度上,提出新的理念与原则;基于此,各平台通过自身或者开源爱好者完成适配转换。
当然,各平台负责方也会加深对统一的认识,借助于 W3C 研讨会、兴趣组、前端会议,促教交流,考虑抽象出自己的渲染 API 与能力通道,让更友好的全能型框架完成高效适配,这块敬请期待吧;中间也必定会产生一些兼容性类库,完成 polyfill 的辅助角色;所以基于这种趋势,快应用采用了这样的路线:开放页面渲染接口,轻松支持第三方的 DSL。
实现方案
那么快应用本次支持 Vue 的 DSL 能力,都做了哪些事情呢?接下来我们从渲染流程、架构设计、开发体验、项目代码、加载过程、平台解耦、测试保证的多个角度阐述。
渲染流程
要想完成适配,首先需要对比两方平台的页面渲染过程是否相似;经过抽象汇总,得出主体过程都是这样的:
步骤 1:工程化工具编译开发者使用某种 DSL 而编写的业务代码。
步骤 2:JS 引擎运行时加载完 DSL 框架后,执行开发者的业务代码。 步骤 3:基于 DSL 的核心逻辑,生成 MVVM 的模型。 步骤 4:业务中对数据的操作,触发对 DOM 节点的更新。 步骤 5:DOM 更新后渲染引擎,发出 VSync 申请,标记脏值节点。 步骤 6:遍历待更新节点,依次样式布局计算、绘制合成,完成渲染。
当前两者实现的区别主要在于:线程的工作分配与协调机制、渲染实现的具体逻辑;然而这些对于 DSL 框架而言,是不需要关注深度实现的。
同时快应用自身会构建一套页面 UI 的 DOM 树,因此抽象出了一套 DOM 的 API 提供给 DSL;DSL 只需要调用快应用提供的节点操作接口,即可轻易完成适配。
为了方便理解,我们在 Github 上增加了 Vue 版本的 TodoMVC 的示例项目 quickappcn/todomvc-vue: https://github.com/quickappcn/todomvc-vue
实际效果可以访问下面地址: https://github.com/quickappcn/todomvc-vue/raw/master/preview.gif
既然流程一致,那接下来就看如何架构设计,分层组织了。
架构设计
以当前支持 Vue 的适配为例,主要工作在于:编译时、运行时两方面;
编译时
提供针对 DSL 的项目模板化、DSL 的解析编译能力,期间可以使用快应用组件与样式的校验解析接口;当前快应用项目的开发,使用的是官方 hap-toolkit 工具,这是一个基于 NodeJS 的 npm 库。 关于项目的构建打包、调试等非 DSL 专有能力的,均已模块独立;项目结构采用模块化的开发方式,借助于 lerna 完成耦合分离;DSL 开发者只需要开发对应的 DSL 模块,增加模板化、语法解析,即可完成适配。
运行时
负责执行开发者的业务代码,管理 DSL 中的驱动模型,完成数据更新到 DOM 操作的转换。 快应用平台运行时会提供 DOM 的 API,针对每个页面提供一个 document 节点。 DSL 层除了包含官方 Vue 的源码逻辑之外,还有两部分:
DOM API 调用:完成对节点的操作; 容器适配模块:提供针对应用 / 页面概念的适配。
针对这块,快应用在 Github 提供了以下几个项目:
1. 项目 quickappcn/Vue: https://github.com/quickappcn/vue 从官方 Vue 站点克隆而来,保存 Vue 核心源码、以及针对快应用 DOM API 的适配。
2. 项目 quickappcn/quickapp-dsl-vue: https://github.com/quickappcn/quickapp-dsl-vue 负责 DSL 在快应用平台上的应用容器适配,如:生命周期、事件通知等。
有了编译时 / 运行时的核心支持,其它工作(如:IDE 支持)就是相对较小的任务拆解了。
开发体
对于使用 DSL Vue 的快应用开发者来说,会不会与标准 DSL(标准 DSL:ux 作为后缀名) 开发方式,差别很大呢? 其实开发过程,与快应用标准的 DSL 项目开发方式基本完全一致,标准 DSL 的项目中展示的 ux 文件,DSL Vue 中展示的是 vue 文件。
使用方式如下: 步骤 1:全局安装 npm 库:npm install -g hap-toolkit 步骤 2:初始化项目:hap init --vue 步骤 3:构建项目:npm run build 步骤 4:运行在快应用的 APK 平台,开发者可以选择“本地安装“或者” 在线更新“的方式,与标准开发方式一致。
项目代码
总结一下,本次快应用为引入 Vue DSL 而提供的项目:
1. 项目 quickappcn/todomvc-vue https://github.com/quickappcn/todomvc-vue 展示在快应用平台上运行该 DSL 项目的实际开发示例。
项目使用了组件化的开发方式,完成展示与表单的页面交互,文件组织结构如下图所示:
熟悉快应用开发的读者,会发现与标准 DSL 一样,这样方便快速上手。
2. 项目 quickappcn/Vue https://github.com/quickappcn/vue
从官方 Vue 站点克隆而来,提供针对快应用 DOM API 的适配;项目中新建了一个 quickapp-initial 的分支,放置适配代码。
3. 项目 quickappcn/quickapp-dsl-vue https://github.com/quickappcn/quickapp-dsl-vue
提供了 DSL 在平台上的应用容器适配,如:生命周期、事件通知等;同时包含针对上一个核心 DSL 源码项目的构建后代码。 为了辅助开发,开发者可以补充测试用例,完成单元测试、项目测试的功能保证。
其中的单元测试:测试 Vue 的自身功能表现正常;其中的项目测试:测试 Vue 在基于 NodeJS 的快应用模拟平台上,是否表现正常。
4. 项目 quickappcn/hap-toolkit
提供对开发者写的 DSL 的模板化、语法校验、项目打包等功能。
采用 lerna 模块化改造后,目前划分的模块的依赖关系如下图所示:
对于 DSL 开发者来说,只需要关注:hap-dsl-vue 的模块即可,这块的代码以源码的形式保存;其中的 templates 文件夹存放项目模板,src 文件夹存放相关的编译解析能力。
对于其他的部分模块,比如:hap-compiler,hap-server 属于所有的 DSL 共用模块,开发者一般无需更新;同时部分模块并未仅提供了编译后代码,如需开放源码,开发者可以下来联系。
为了保证稳定性,也可以增加测试用例(当前使用的是 Jest),完成单元测试与项目测试的编译功能确认。
hap-toolkit@0.3.0 版本上增加了对 Vue DSL 的支持,不过并未采用 lerna 管理,后续发布的 0.4 版本以后会用这种方式。
注意:由于新业务功能的开发,当前模块化组织结构可能还会继续调整。
加载过程
在快应用完成编译时 / 运行时的开发后,DSL 是如何加载并调用渲染的呢?大家看下下面的图例就明白了:
快应用的运行可以分为三个阶段:
第一阶段:环境准备
底层平台启动,暴露 DOM 等相关 API,加载 DSL 代码,并完成与平台的桥接通讯。
第二阶段:业务代码执行
加载并执行开发者项目中的 vue 业务代码(编译后转换为 JS),建立驱动模型,完成 VDOM 的对比。
第三阶段:平台渲染
上一层 VDOM 对比的结果,转换成对平台的 DOM API 的实际调用,平台线程然后做布局计算、绘制等完成界面的展示;上图所示,可以得出:DSL 与平台的解耦与交互发生在第一阶段的最后一步,即:平台接口暴露之后,业务代码执行之前;因此整个运行,DSL 框架仅会加载一次。
平台解耦
那么 DSL 与平台需要考虑哪些方面的解耦事项呢?主要分为三个部分: 1)容器管理
快应用是一个应用形态,包含多个页面,这点不同于浏览器,所以就会存在应用 / 页面的生命周期。
开发者需要监听这些生命周期,用于完成:数据请求、统计、性能监控;为了保持解耦合,平台使用了 Publish/Subscribe 模型,DSL 只需要订阅相关的事件,即可暴露给开发者。
开发者可以从项目 quickappcn/quickapp-dsl-vue 的 src/shared 文件夹 中得到提示。
2)页面渲染
对于每个页面来说,页面的渲染依赖于组件树的构建,为了方便对组件进行操作,平台提供了一套类似浏览器的组件操作接口,称为:平台 DOM API;为了提升 DSL 适配的简易性,快应用的 DOM 与浏览器中的 DOM 非常相似。
比如:创建节点的 API(document.createElement())、节点增删的 API(element.appendChild()、element.insertBefore())。
在 Vue DSL 中,开发者都会使用哪些接口进行节点操作呢?
可以从项目 quicappcn/vue 的 src/platforms/quickapp/node-ops.js 文件中得到提示。 3)接口功能
业务开发中,开发者肯定需要调用系统功能,如:fetch 请求:require('@sysem.fetch')、地理位置:require('@system.geolocation')。
这方面的语法与平台的标准 DSL 语法保持一致,会在编译时进行转换,如:fetch 请求转换为:$app_require$("@app-module/system.fetch")$。
平台执行开发者的 JS 代码时,会自动注入一个全局函数 $app_require$;那么 JS 执行时就会通过该函数完成系统功能的获取。
所以关于这块,DSL 适配不会有实际工作量;可以项目 quickappcn/quickapp-dsl-vue 的 src/dsls/vue/page/interface.js 文件中得到提示。
测试保证
对于 DSL 开发者来说,通过测试用例保证功能稳定是必不可缺的;针对编译时,测试相对简单,查看项目 quickapp/hap-toolkit 即可读懂;针对运行时,如果每次对源码修改后,都需要在手机设备上测试运行,确认功能的话,将会很浪费时间;
为了解决这一难题,快应用平台的前端层面,提供了在 NodeJS 环境上的平台模拟能力;因此开发者可以通过两方面的测试用例来保证稳定:
单元测试
完成对 DSL 的基本功能进行测试,如:指令、filter、数据驱动等各种 DSL 自身特性。
相关代码请参考:项目 quickappcn/quickapp-dsl-vue 的 test/suite/dsls/vue/unit 文件夹。
测试命令请参考:项目 quickappcn/quickapp-dsl-vue 的 package.json 中的"test:suite:framework:main:vue:unit"命令。
项目测试
开发者像开发正式的快应用项目一样,编写 DSL 页面;模拟平台会将测试项目编译打包,然后逐个执行开发者的页面代码;开发者可以在这里,测试 DOM 树的结构一致性、生命周期、接口功能等。
相关代码请参考:项目 quickappcn/quickapp-dsl-vue 的 test/suite/dsls/vue/project 文件夹。
测试命令请参考:项目 quickappcn/quickapp-dsl-vue 的 package.json 中的"test:suite:framework:main"命令。
合作交流
如果您是快应用的开发者,或者其他角色,对于前端开发生态感兴趣,欢迎提出各类建议,或合作意向。 快应用平台对 DSL 能力的支持,前端与底层研发团队做了很多耦合分离工作,开源工作得以推进;同时联盟内各家厂商研发鼎力支持,共同管理项目源码与规范制定。
使用DSL开发
如果您也是 Vue 框架深度爱好者的话,可以考虑使用 Vue 来做快应用产品的开发。
围绕 Vue DSL 的最新能力与运行体验,我们将会在项目 quickappcn/quickapp-dsl-vue 中保持更新,您可以向这里提交 issue 反馈需求。
快应用的 Vue DSL 会在 1050 版本邀请内测,待功能稳定后,平台将内置正式版本的 Vue DSL。
|