2018 年 10 月 13 日,又拍云 Open Talk 丨2018 小程序开发者沙龙系列活动广州站拉开帷幕,金山办公前端开发工程师段炼在沙龙上做了《WePY 框架开源实践》的分享。“2018 小程序开发者沙龙”是又拍云 Open Talk 继“2018 音视频技术沙龙”后推出的重磅活动,与大部分偏重营销、流量的小程序活动不同,本系列活动更热衷于分享小程序开发过程的种种有趣经历和有益的经验。
段炼,金山办公前端开发工程师。热爱开源与分享,小程序组件化框架 WePY 核心贡献者,对小程序性能优化、高效开发有较深入的见解。目前就职于金山软件,负责 PPT 美化前端应用的基础架构建设及开发工作。
下面是演讲内容:
本次分享主要包含下面三个部分:
什么是 WePY
- WePY 的未来规划
- 开源的经验分享
分享之前,先聊聊我在开源项目上的一些经验。
上图中可以看到过去一年内,我在 WePY 项目提交代码的频率,近一两个月尤为频繁。我是这个项目的核心构建者,平时参与 WePY 的维护工作。
小程序出来不久后,我就接触了 WePY,尝试用小程序制作一款应用,过程中遇到很多痛点问题,我尝试用脚手架工具搭建工作流,期间发现 WePY 这款框架能够很方便地解决所遇到的问题。后来,我机缘巧合认识了 WePY 的作者,由此走上了开源的“不归路”
2017 年年底,小程序出了一个原生组件,让 WePY 的应用场景比较有限。我们思考着做一个大的重构,也就是现在说的 2.X 的版本,基于小程序特性做优化。2018 年 1 月,我设计了一个命令,能够快速生成项目的脚本架,到了 2018 年的 5 月,腾讯邀请我去做分享,并颁发了核心贡献者证书。2018 年 10 月,我组织了一个 2.X 版本内测活动,大概有 30 个开发者参与。这是我在开源上所做的一些事情,下面就正式进入主题,给大家介绍 WePY 。
WePY 是什么?
前端开发者肯定对 Vue.js 和 Webpack 这两个开源项目非常熟悉。Web App 或 H5 开发过程中,我们常常将 Vue.js 用作核心库,用 Webpack 做模块化打包,让其能够运行于浏览器端。那么 WePY 是什么东西呢?我们可以把 WePY 理解成 Web 端的 Vue.js 和 Webpack 的结合体,它能够通过编译手段运行在小程序端,并且可以使用 Vue.js 的一些语法和特性。
WePY 的功能与特点
首先,WePY 能够通过简单的命令生成项目的脚手架,也就是说通过一个命令能够快速搭建项目,而不用去做烦琐的配置。第二,编译打包。如果我们采用原生开发模式,经常会使用比较高级的特性,会使用 gab 做简单的编译处理,而 WePY 可以提供 language 预编处理,APM 打包等功能。第三,核心库。这块其实和 Vue.js、React 等框架是类似的,做了 API 封装,比如说事件机制的封装。
说了 WePY 的功能,再说说 WePY 的特点,主要是以下三个特点。
- 特性与优化:WePY 对原生小程序框架的特性做了优化与补充,例如在开发小程序过程中很容易产生性能问题,比如频繁操作 SetData,而在 WePY 中不用关心这块。
- 复用与扩展:WePY 初期解决的是组件化问题,它可以抽象出一些组件让我们复用,包括 language 编译器,甚至自己去定义一套,也包括一些功能插件、压缩代码混淆插件。
- 多端:相信大家都经历过一个痛点,在开发 Web 端应用时,业务需求需要把这个应用移植到小程序端。由于小程序端和 Web 端代码是不通用的,这时需要把 Web 端逻辑抽出来,放到小程序,让它能够运行到小程序端,而 WePY 提供了多端的解决方案。
WePY 的实现原理
WePY 在小程序开发中解决几个问题:
第一,组件化开发。初期小程序不支持组件化,当我们想把一个功能转移到另一个小程序,需要把那部分代码复制拷贝出来,然后引入到另一个小程序。而在组件化后,我们可以直接把功能的组件拿过来用。
第二,解决 NPM 资源问题;
第三,性能优化,避免我们去做频繁的 Set Date 操作;
第四,支持多端。
△ WePY 的架构图
WePY 主要核心的有两块,编译和核心库。
编译模块分为几个核心编译部分,一个是小程序端的编译模块,WePY 是我们实现多端时转换成 Web 端代码所需的编译模块。在它上面抽象了两个概念,Compllers 和 Plugins。Compllers 中需要使用一些高级的语法,例如 Less,TypeScript 等,在这会做预编、预处理的工作。编译完成后,会经过功能插件即 Plugins,做代码的压缩、替换和混淆等。这是 CLI 的部分。
核心库,也是运行在小程序 wrong time 的一个抽象,分为 WePY、WePY-Web 和 WePY-weex。WePY 这部分抽象了一些细节,例如 Mixin,Event 之类。WePY-Web 在WePY 的基础上,还有 Web-API,Web Components。简单解释下这些是干嘛的,小程序原本自带一些功能组件,比如说 Swap,icon,视图容器等,我们要在浏览器运行起来的话,需要去使用原生小程序内置的一些组件功能。
Web-API,将小程序的一些方法做转换。上面部分的 WeChat API 和 QQ API 主要是适配的作用,小程序 API 在浏览器无法实现像登陆或者调用分享之类的功能,通常开发 Web 端经常要去微信端或者 QQ 端运行,这个可以通过底层 js SDK 的能力去做到。上面是我们一些生态,包括第三方组件,第三方模块,比如数据上报,状态管理之类的。
△ WePY 的编译过程
我们在 WePY 里定义了语法规范,有一种新的文件 .wpy 。在编译之前会梳理组件引用关系,然后我们会把这个当文件做拆分,拆成样式、模版、脚本、config 等,再经过预处理做编译,生成小程序四种类型的文件,而后经过 Plugins 做压缩,生成终极代码。
多端的实现一开始遇到很多挑战,比如原生小程序转化成 Web 端代码非常困难,而使用 WePY 作为中间维护的代码,生成小程序端、Web 端的代码是可行的。但是会遇到以下几个挑战:
- 开发模式:原生小程序开发模式和 Web 端开发模式是不同的,WePY 是类 Vue 的框架,它的开发模式和 Vue 类似。
- 标签与样式:小程序有自己的一套标签,和原生 Web 有差异性,但是可以通过简单的转化来解决这个问题。
- 模块语法:小程序拥有自己的模板语法,可以通过简单的处理,把它转化成 Vue 的指令。
- 模块化:Web 端原生不支持模块化,而是用 required 加载代码,我们可以参考业界常用的代码导入方案 Webpack 等,这个问题也是可以解决的。
- 内置组件:也就是刚说的 Web Components ,我们需要使用 Vue 实现一套小程序内置组件的功能。问题是内置 API,这块我们可以借助 QQ 或者微信端的 js sdk,做适配处理。解决这些问题以后,就能实现多端的解决方案。
生态部分在 GitHub 上搜索 WePY 可以找到很多资源,有一些社区上的 UI 模板,也有用户上传的第三方组件。
WePY 未来规划
这是讲 WePY 2.X 的这个部分,首先来了解下 WePY 1.X 有哪些问题。
- 静态编译组件。1.X 版本用静态编译方式来处理组件,会导致使用动态 Repeat 时产生一个重大的 bug,这就是 2.X 的版本需要解决的问题。
- 语法解析。1.X 并没有使用 AST 的语法解析,这导致当开发者写的代码不是特别规范时,会导致一些 bug。
- 类 Vue 语法。更多用户希望它能够尽快和 Vue 保持一致,虽然 WePY 是类 Vue 的框架,但是它有很多设置和 Vue 有区别。
- 数据绑定性能优化。1.x 版本使用章检查的方式,这其实还有可优化的空间,比如使用Vue 的一套数据劫持方式做优化。
- 错误处理机制。WePY 开发过程中,经常会报错误,但是并不能清晰的看到是什么错误,具体是哪一行报错。
- 测试用列覆盖度。在 1.X 版本中,只有核心库做全覆盖,而编译部分是没有的,这导致了很多问题,2.X 版本会着重处理这块,保证 100% 的覆盖率。
书写方式与生命周期
左边这个图是 2.X 版本的书写方式,它和 1.X 版本有哪些区别?模板可以用 div,HTML 原生标签样式去写,这些是 Vue 内置指令。组件是基于原生组件去实现的,script 脚本和之前不一样,采取对象自变量方式传入。
生命周期这块简单说下,首先我们在 wepy.component 或 wepy.page 时去做数据 patch,进入生命周期的初始化,总的来说 2.X 和 1.X 版本生命周期差别不是特别大,主要继承现有原生组件的生命周期。
组件
1.X 和 2.X 版本中组件的实现方式不同,1.X 是基于静态编译,2.X 是基于原生组件,1.X 版本中在同一个 page 里使用多次组件会产生共享问题。WePY 2.X 采用是基于原生组件这一套方式,原生组件有自己的独立作用域,在这个方面做得很好。循环组件刚才有提到,在 1.X 版本有很大的坑,在 2.X 版本中可以用 Vue 的内置指令,让组件作为循环。生命周期这块,2.X继承原生组件的生命周期。
△ 组件的包含关系
图中的绿圈指原生开发模式支持组件情况,蓝圈是指 WePY 开发模式支持的组件情况,原生支持本地资源以及 NPM 包上面的原生组件。WePY 除了支持本地的 WPY 为后缀结尾的组件,以及 NPM 上面以 WPY 为后缀的组件,还同时支持原生组件。也就是说我们目前采用原生开发模式。积累了一些原生组件,我们就能轻松切换到 WePY 开发模式,因为这些组件是能够复用的。
编译
WePY 1.X 基于文件编译,在编译前会检查有哪些文件,如果检查到 WPY 文件,经过 compiler 处理,再找到 plugins,生成小程序端运行的文件。
WePY 2.X 基于入口做编译,它先找到 App 的入口,然后去解析有哪些页面,然后向下寻找,2.0 版本中没有 compiler 这个概念,所有里面的东西都是插件,包括之前的预处理和编译,甚至包括一些功能性的插件等,输出一个小程序格式的文件,还有 Vendor 的资源,Assets 在项目里面引用的模块,Static 是我们配置的静态资源,它会做简单的复制处理。
WePY 2.X 使用插件化机制,就如墙壁上有很多钩子,我们不知道钩子上挂了什么,我们只需要关心把所有钩子串起来制定一个流程,这就是 WePY 编译过程。首先,初始化配置,然后进行编译输出文件。这样做有什么好处呢?其实这里参考的是 Webpack,因为它做的是高度插件化处理,这样做可以去实现高拓展性的耦合。怎么去检验它呢?开发者使用 WePY 编译过程中,发现它有一些问题,或者不满意编译的结果,我们可以自己写插件,在编译阶段去做一些钩子上面的实现,然后直接替换掉官方编译的处理,也就说可以用自己插件侵入到它的内部。
数据绑定
这是列的是 WePY V1 章检查的数据管理机制。举个例子,老师拿三个文件 ABC 让小明去做修改,同时老师也备份了这三个文件 ABC,小明对文件 B做了修改,老师坐在旁边陪着小明,小明修改了文件 B 后老师把小明修改的三个文件拿回来,三个复制文件做了对比,发现 B 文件做了修改,然后把 B 文件放在袋子里面,这是一个同步的流程,因为老师守在小明身边,还有一种异步流程,老师不在小明身边怎么办?于是老师喊来小明的爸爸大明陪着,在小明修改完文件 B 后,大明把文件交给老师,然后老师做对比,然后把修改的放在袋子里。
这是 2.0 版本做了数据绑定的处理。在这里小明是一个非常聪明的学生,他修改了文件的同时会记录修改了哪些文件,修改了哪些位置,修改完毕以后,会主动告诉老师我把哪些文件改完了,修改了哪些部分。
如何保证 WePY 2.X 中的质量呢?首先是内测,我在国庆期间组织了内测的活动,大概有 30 个开发者参与这次活动,非常感谢开发者积极的发现并提出问题,甚至有一些开发者会主动去完善这些功能,提供了非常多的帮助。第二,测试用例覆盖度的问题,在 2.X 这个版本,我们会着重处理这块,保证 100% 的测试覆盖率。
开源经验分享
日常业务开发当中面临上线的压力,文档规范不一定做得那么好,因为我们急着要上线。做开源项目的时候,特别需要注意文档规范。因为用户首先会看文档怎么样,如果文档写得很模糊或者只是放了代码上去,甚至没有写,那开发者肯定不会用它,觉得这个开源项目不太靠谱。测试 CI 还有版本更新状态,很多开源项目有一些很小的图标,这个主要是体现仓库的信息,能够让开发者放心地使用开源项目。如何安装使用,如果你不想一个人做一个开源项目,想让更多人开发者参与进来的话,就要做好这方面的工作。
License 也是一开始需要注意的,这个项目使用什么协议,否则做到后面一不小心成了谷歌和甲骨文这样的关系。代码规范也是一个问题,在开源项目里面,我们需要严格保证代码规范包括一些注释,书写规范等。在 WePY 里,我们参考了 Git 的提交规范,这方案已经相对成熟,并且有一些配套的工具可以使用。
工程化:WePY 项目里使用了 Mocha 做单元测试的工具,Istanbul 做覆盖率测试。集成测试也非常重要,在大型开源项目中会有不少开发者提交补丁,但我们不能够保证他们提供代码符合规范,或者不清楚这段代码有没有破坏性。借助 Travis CI 我们可以时时拿到反馈,因为在出发的时候会运行 test 之类预制命令。后面就是部署,WePY 里总共有将近 20 个包,我们使用了 Lerna 工具做多包的发布管理。
以上是我今天要分享全部的内容。想说的是小程序从上线以来,社区涌现了大量的工具,这足以说明小程序开发非常火热,前景非常好,同时它也需要有 WePY 这样的工具让大家能够进行高效的开发。也希望大家能够在开发过程中能够参与进来,参与到开源项目中来,能够帮助到大家或者也能够帮助到自己。