“青芒小程序+”是青芒团队提出的小程序解决方案,将帮助内容创业者免费搭建属于自己的微信小程序。在开发“青芒小程序+”等小程序应用的过程中,本文作者及其团队对当下火爆的小程序开发有了更深入的了解和认知,从而促成了本文的创作。
自小程序诞生以来,很多人开始研究其机制和特点,已经有很多不错的文章可以从源码或整体架构的角度让人受益。但理论是一回事,要真正理解小程序,还需要实践,进一步了解其背后的理念,与现有平台的异同,以及如何适应它们并做出更有趣的小程序。
了解一个开发平台的特点的一个好方法是从编程模型开始,看看在这个平台上开发时如何编写和组织你的代码,然后弄清楚三个问题:
数据如何获取;界面如何呈现;交互如何进行。
也就是说,就是从MVC(-View-)的角度去拆解这个平台的特点,才能了解它开发的特点。
如何获取数据
程序的本质就是数据的呈现和处理,所以看一个客户端开发平台的基本能力,首先要看它能放什么数据在上面处理,它的局限性是什么。如果缺少了必要的数据获取手段,那么对于开发者来说,再好的厨子也是无米之炊。
从这个来看,小程序提供的数据获取方式非常丰富,大致涵盖:
从上面的介绍我们不难看出,小程序中数据获取的方式和一般浏览器提供的数据获取方式(即应用能够获取到的信息)差不多,相比原生客户端有一定限制,但对于大多数应用来说已经足够了。
另外,小程序提供了微信生态里的一些数据,比如账号信息等,这只是庞大的微信生态里非常小的一部分数据,但却是开发小程序应用最有价值的数据。
例如在其他平台,如果要获取微信账号信息,需要经过一次用户授权,如果用户暂时不愿意提供,程序就会出现“未登录”状态,给整个服务的开发带来困难。而在小程序中,只要用户点击,就代表授权完成,开发者可以直接读取小程序的账号信息,并作为用户身份同步到自己的服务器,从而实现“一直登录”的状态,以便更好的提供后续的服务。
一个可能的示例如下:
代码语言:
复制
// 先调用登录接口,获得请求码 wx.login({ success: function (res) { // 获取到请求码,继续请求用户的基本信息 var code = res.code wx.getUserInfo({ success: function (res) { // 获取到了加密的用户信息,去服务端解密并存储 var userData = res.encryptedData var iv = res.iv wx.request({ url: 'https://my_account/...', data: { code: code, user_data: userData, iv: iv }, success: function(res) { // 在服务器上,解析并生成自己的账号验证信息 var user = res.data.user var token = res.data.token // 并且还可以存在本地存储上,供下次打开使用 wx.setStorage({ key: 'my_token', data: token }) } }) } }) } });
界面的呈现方式
小程序刚出来的时候,很多人开始惊呼这个时代来了,因为小程序在界面层用到了HTML/CSS/技术栈。但很快,随着聪明的程序员对小程序的深入了解,发现小程序里的HTML/CSS/和Java里的是完全不同的东西,这种区别基本上和Java和Java的区别是一样的。
在小程序中,HTML 对应的是 WXML,只保留了 HTML 的概念,而传统的
、 标签被彻底抛弃。与 类似,小程序也引入了自己的 HTML 标签。与 、 等语义化标签不同,小程序中的标签更像是传统客户端开发中的组件(或控件),每个组件都有自己的功能和用法。比如需要展示一张图片,就只能用标签,不能用其他的,需要提供可选的文本,就只能用标签等等。
这种方式最大的问题是传统的 HTML 页面无法在小程序中呈现(小程序也不提供类似的客户端控件),比如有大量的内容网站,文章内容以 HTML 片段的形式存储,无法直接在小程序中呈现。如果需要展示,一种思路是搭建一个中间服务,将 HTML 转译成更简单、更容易渲染的中间格式数据,再将中间格式数据转换成小程序标签在小程序端呈现。我们在做《青芒生活》的时候,恰好设计并实现了一个将任意 HTML 页面转译成中间格式(内部名为 RAML)的转义服务,解决了内容 HTML 页面在小程序上的呈现问题,如图 1 所示。
图 1 在小程序中呈现 HTML 内容页面
相比于HTML,小程序的WXSS相对完整地保留了CSS的特性,这一点还是比较出乎意料的。WXSS在语义上最大的不同就是支持相对尺寸单位rpx(),相当于当前设备的屏幕宽度。它的引入让原本复杂的屏幕尺寸适配变得简单很多。另外一个与CSS的不同就是,它更像是传统的控件样式使用方式,不像CSS3那样支持那么多的选择器,更多的时候是每次只使用一个控件。

小程序虽然支持了 ES6 标准,但是窗口级别的标准被彻底抛弃,开发者无法通过窗口级别的标准来调用和修改界面元素完成逻辑。小程序其实直接对应的是 Node.js 的使用方式,用于完成后台业务逻辑,而不是直接控制交互。小程序的这种设计让它可以使用 Dom 的方式来渲染界面,使得在更新界面数据时可以优化性能,但付出的代价就是缺少了窗口级别的粘合剂,使得很多功能的开发变得极其死板和复杂。
如何进行互动
所谓交互传递,是指用户与界面交互时,平台框架以何种方式告知业务层,并将处理后的变更呈现回交互界面。如果把 WXSS+WXML 绘制的页面看作“前端”,把写好的业务逻辑看作“后端”,就会发现小程序的前后端交互特别类似 Web 1.0 模式。前端将交互行为封装成事件()发送给后端,后端处理完成后再通过方法将数据传回前端,如图 2 所示。
图2 小程序交互
小程序提供了单击、长按、触摸、滑动等基本功能,对于视频播放器等控件,还有播放、暂停的监听。这些事件都比较基础,没有更高级的手势、多点触摸等相关事件,但足以让开发者详细了解用户的输入并做出响应。小程序响应界面的唯一方式就是通过 Page 中的 API 更新界面上的数据,小程序会对比两次调用之间数据的变化情况,决定需要更新交互界面的哪一部分。
举个例子,假设开发者需要做一个滑动页面切换的效果,在小程序中该如何实现呢?首先在渲染页面中引入变量数据:
代码语言:
复制
可以看到, 是一个模板参数,其初始值为0,表示移动的距离。事件通过绑定到函数上的方法返回。
代码语言:
复制
movePage: function(event) { var status = { needUpdate: false, distance: 0 } // 处理各种事件,计算是否需要刷新,和移动方向 if ("touchstart" === event.type) { // 开始计算移动 ... } else if ("touchend" === event.type) { // 判定移动的距离是否足够. ... } else if ("touchcancel" === event.type) { // 被打断就算了. ... } else if ("touchmove" === event.type) { // 计算移动距离 ... } // 根据移动的距离,来更新界面 if (status.needUpdate) { this.setData({ distance: status.distance }) } }
一端捕获事件,计算偏移量,并将新的偏移量发送到前端接口。
由此我们可以看出,小程序的交互是典型的单向模式,前端返回事件、数据都是单向推送给前端,而不是通过“变量”、“状态”等方法来通知。在这种模式下,开发者往往无法非常精准地掌控界面变化,整个核心都依赖于小程序对两次数据变化的 diff 计算,最终会影响整个交互的性能。
小程序开发模式特点
至此,我们可以总结出小程序开发的一些特点。整体来说,小程序采用了 的技术栈,实现了传统的客户端开发模式。这和其他平台比较类似,可以算是一个新的分支。
从设计上看,小程序有很多“限制”,其中最大的限制就是开发者无法通过这样的编程语言直接控制界面,而是通过数据驱动来间接控制。这对于缺乏开发经验的人来说是有利的,因为降低了理解的门槛,但对于复杂的应用来说,这种模式开发起来比较死板,往往一次改动需要多次修改,增加了代码的理解成本。
开发小程序的陷阱
开发小程序的日子,也是一场踩坑之旅。简单总结一下,小程序的坑大概来自以下几个方面:
总之,作为一个新的开发平台,微信小程序在自身的稳定性、配套的工具链等方面还不够完善,对于早期开发者来说,需要付出额外的努力去尝试和探索,但这或许就是一个新平台的价值与代价。