全程干货!以实践真正理解微信小程序

2017-01-04

来自公众号:前端之巅

本文是群里的一个朋友推荐的,我细度了一下,确实干货不少,十分值得推荐,感谢分享者和作者;

一、实践背景

「轻芒小程序+」是由轻芒团队提出的小程序解决方案,它将替内容创业者免费搭建属于自己的微信小程序,其创建的小程序在内容发布之外,还将具有评论、笔记、付费阅读等特色功能。轻芒小程序+ 不打算创建一个新的内容平台,而是将内容创业者现有的自媒体账号转化成微信小程序。内容创业者只要照常更新自媒体,这些内容就能自动更新到小程序。

我们在做「轻芒小程序+」和其他轻芒产品的小程序应用过程中,对小程序有了进一步的理解,进而有了本文。 

二、以实践来理解小程序

从小程序诞生伊始,就有很多人开始研习小程序的机理和特点,从源代码的角度、从整体架构的角度,有很多不错的文章会令人受益。

但理论是一回事,真正理解小程序,还是需要一定的实践,才能进一步去理解小程序背后的一些想法,它和现有平台的一些异同,以及如何去适应它,做出更有趣的小程序。

去理解一个开发平台的特性,一个不错的角度就是从 编程模式入手,就是看在这个平台上去开发,需要如何书写和组织自己的代码,进而搞清楚三个问题:

  • 数据如何获取

  • 界面如何呈现

  • 交互如何传导

换而言之,就是从 MVC(Model-View-Controller)的视角去拆解这个平台的特性,从而理解在这个平台上开发有何特点。

1. 数据如何获取?

程序的本质,可以说就是数据的呈现和加工。所以,看一个客户端开发平台的基本能力,首先,就要看能把哪些数据放在上面处理,有哪些局限性,如果缺少了必要的数据获取方式,那对于开发者而言,巧妇也难为无米之炊。

从这点看,小程序是提供的数据获取方式算是非常丰富了,大概涵盖:

  • 通过 HTTPS 请求去服务端获取数据 。支持 HTTP 是最基本的,小程序对 HTTP 有限制,除了要求通信协议是 HTTPS,出现的域名必须提前预设之外,还将应用层协议限定到了 Json 格式下,这一点,可能比任何一个已有客户端平台更为严苛。站在小程序的平台角度来看,通过这样的协议规定,对应用中流动的数据有了更强的管控能力;而对于开发者而言,则需要花些时间去调整自己的服务协议以便适应小程序的要求。

  • 可以在本地文件系统上存取数据 。小程序提供了算是丰富的 APIs 供开发者在手机系统上存取文件。开发者可以本地文件来做缓存、做状态记忆,等等,为开发提供了不错的便利。

  • 可以读写设备中的一部分信息 。小程序开放了一些 APIs,帮助开发者获得设备上的一些基本信息,比如:手机型号、屏幕尺寸,网络状态,等等。比较有价值的,是可以选择获取手机上的图片等多媒体文件,这给做一些图像相关的应用提供了可能性;以及它还提供了不少设备上罗盘、重力感应器、地理位置等相关的信息,对开发者理解用户所处的环境有很大帮助。

从上面的介绍不难看出,小程序中的数据获取方式,和一般的浏览器提供的相仿(也就是和做 HTML5 应用能获取的信息),比原生的客户端更局限一些,但对于绝大多数的应用而言,是足够用了。

除此之外, 小程序提供了微信生态中的一些数据 ,比如账号信息。这对于微信庞大的生态而言,只是非常小的一部分数据,但确是开发小程序应用中最值得利用的一部分数据。

举个例子,在其他平台上,如果需要获取到微信的账号信息,需要通过一次用户授权。如果用户暂时不想提供,则会使得程序出于 “未登录” 状态,给整个服务的展开带来困难。而在小程序中,只要用户点开小程序,就意味着完成了授权,开发者可以直接读取到小程序的账号信息,并可以同步到自己的服务端作为该用户的身份标识,从而实现 “始终登录” 的状态,使得后续服务可以更好的提供。

一份可行的示例如下:

// 先调用登录接口,获得请求码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                      })
                    }
                })
            }
        })
    }});

2. 界面如何呈现?

小程序刚发布的时候,一片人开始惊呼 HTML5 的时代就要到来了,因为小程序在界面层,使用了 HTML/CSS/JavaScript 这套 HTML5 的技术栈。但很快,随着聪明的程序员们对小程序的理解进一步加深,就发现小程序所说的 HTML/CSS/JavaScript 和 HTML5 中的完全不是一回事,其差异,基本等同于 Java 和 Javscript 间的差距。

在小程序中,和 HTML 对应的是 WXML ,它保留下来的只有 HTML 的概念,而传统的 <div>  <a> 标签都完全被抛弃了。和 Facebook 的 React 类似,小程序引入了自己的 HTML 标签,它和 <article>  <section> 这样的语义标签不同,小程序中的标签,更像是传统客户端开发中的 组件 (或者叫控件),每个组件都有自己背后的职能和使用方式。比如:如果需要展示图片,就只能用 <image> 标签,其他标签都无法承载,而如果需要提供可选的文本,则只能使用 <text> 标签,等等。这样的方式带来最大的问题是传统的 HTML 页面都无法在小程序中呈现(而小程序正好,没提供类似 Web View 的客户端控件)。

比如,大量的内容网站,其文章内容都是存储为一个 HTML 片段的,这样就无法直接呈现在小程序中。如果需要展示,一个思路是构建一个中间服务,将 HTML 转译成一种更简单利于渲染的中间格式数据,然后,在小程序端把中间格式的数据转换成小程序的标签进行呈现。我们在做 轻芒生活 的时候,正好设计并实现了一个转义服务,将任意一个 HTML 页面转换成中间格式(内部名是 RAML),解决了内容性 HTML 页在小程序上的呈现问题。

全程干货!以实践真正理解微信小程序

在小程序中呈现 HTML 内容页

和 HTML 相比,小程序的 WXSS 算是比较完整的保留了 CSS 的特征,这一点还蛮出乎意料的。WXSS 在语义上最大的不同,一是在于它支持了相对尺寸单位 rpx ,每 750rpx 等价于当前设备的屏幕宽度。这个相对尺寸单位的引入,把那种繁复的屏幕尺寸适配变得简单了不少。而和 CSS 的另一个不同,是它更像传统控件样式用法,不支持 CSS3 那么多的选择器,使用中,更多的是一个控件一个 class 这样来使用。

小程序中虽然支持 ES6 标准的 JavaScript,但窗口级的 JavaScript 在小程序中完全被废弃掉了,开发者无法用 JavaScript 去调用 window、document 对象来修改界面元素完成逻辑。小程序中的 JavaScript 其实直接对应 node.js 的用法,用来完成后台业务逻辑,而不是直接控制交互。小程序的这个设计,使其可以用到 virtual dom 的方式来渲染界面,让界面数据更新时的性能优化成为可能,但付出的代价就是少了窗口级 Javscript 的那层胶水的黏合,使得很多功能的开发变得极其呆板和繁复。

3. 交互如何传导?

所谓交互的传导,是当用户和界面发生交互式,平台框架通过何种方式告诉业务层,并将处理后的变化呈现回交互界面上。如果把 WXSS + WXML 绘制的页面看成 “前端”,把 JavaScript 撰写的业务逻辑看成 “后端”,你会发现,小程序的前后端交互特别像 Web 1.0 的模式,前端把交互行为封装成 事件(event) 发送到后端,后端处理完成后,通过 setData 方法将数据回传到前端。

全程干货!以实践真正理解微信小程序

小程序的交互传导

小程序提供的 Events,基础的有类似单击、长按、触摸、滑动,等等,对于视频播放器等控件,还有监听播放、暂停等等。这些事件涵盖算是比较基础的,没有更高级的手势、多点触控等相关事件,但也还是足够让开发者具体了解用户的输入,进而做出响应。

而小程序给界面响应的唯一方式,是通过 Page 中的 setData API 对界面上的数据进行更新, 小程序会比较两次调用期间数据的变化 ,来决策需要更新哪部分的交互界面。

举个实际的例子,假设开发者需要做一个滑动切换页面的效果,在小程序中该如何实现?首先,是将变量数据引入渲染页面:html <view class="page" id="current-page" style="left:{{distance}}rpx;" bindtouchstart="movePage" bindtouchcancel="movePage" bindtouchmove="movePage" bindtouchend="movePage"> </view>可以看到, distance 是一个模版参数,它初始值为 0,表示移动的距离。通过 bindtouchstart 等函数绑定上 JavaScript 的方法,将事件回传。

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        })
    }}

而在 JavaScript 一端,则捕获事件、计算偏移量,然后将新的偏移量送到前端界面。

从这里可以看到,小程序的交互模式,是典型的单向模式,前端回传事件,数据单向的推到前端,而不是通过类似 “变量” “状态” 这样的方式来告知。这样的模式下,开发者对界面变化的控制往往不可能太精准,整个核心,都依赖小程序对两次数据变化的 diff 计算,这个会最终影响整个交互的性能。

三、小程序开发模式的特点

至此,我们可以来总结一下小程序开发的一些特点了。整体来看,小程序是借了 HTML5 的技术栈,行了传统客户端开发的模式,这一点和 React 等平台会比较相近,可以视为 HTML5 的一个新分支。

从设计思路看,小程序做了大量的 “限制”,最大的限制,是开发者其实是无法通过 JavaScript 这样的编程语言,直接对界面进行控制,而是通过数据驱动来间接实现。这对于缺少开发经验的人而言,是有益的事情,因为这降低了理解的门槛,但对于复杂的应用而言,这个模式开发起来比较呆板,往往是一个变化多处修改,增加了理解代码的成本。

四、开发小程序的坑

开发小程序的日子,也是一个踩坑的历程。简单总结,小程序中的坑大概来自这几个方面:

  • 和 Web 的兼容性 。小程序引入了 HTML/CSS 做为技术栈,并在其基础上进行了定制。很多开发中的问题都来自于 “定制”,因为你并不知道哪部分就被定制了,哪部分是被继承了。比如,你用了一个 CSS 语法,发现并不生效,或者效果和浏览器中的不一样,于是,你只能换一个写法,结果很有可能,又会继续发现,这个新的写法可能效果也不对,于是你只能继续尝试,如此反复,可能会消耗大量的时间。

  • 开发环境不稳定 。小程序的开发,是基于微信自制的一个 IDE,但当下,IDE 的稳定性、易用性都非常之差,时常出现 Bug,你以为是你的程序写错了,但其实,是 IDE 的 Bug,重启一下 IDE,一切都引刃而解了。于是,当你日后开发小程序时出现某种异样,先重启 IDE,再看问题还在不在,也许是种更节省时间的方式。

  • 缺少真机调试环境 。小程序的运行时其实就是微信,微信几乎没提供任何真机上调试工具给你(也不能说完全没有,有一个只能在真机上瞪着眼睛看的日志框)。你在模拟器中调试好的程序,可能在真机上运行起来并不如预期。比如,我们碰到过真机上白屏、位置错乱、动画效果不对,以及 Android 上至今还不能运行,等等问题。这对于稍微复杂的程序而言,颇为梦魇,想做一些细粒度的调整和优化,基本只能靠猜。

  • 闭源且缺少学习资料 。小程序整体上是闭源状态(虽然模拟器和 IDE 部分可以通过反编译来看),且缺少足够的学习资料,如果一旦碰到控件如何使用、为什么这么用不对,之类的问题,就只能靠不停的试来解决,也需要耗费大量时间。

五、总结

简而言之,做为一个新的开发平台,微信小程序从本身的稳定性,以及配套的工具链上都不算完善,这对于早期开发者而言,需要耗费额外精力去尝试和探索,但这也许就是一个新平台的价值和代价吧。

精彩问答

淀粉提问:将已有业务用小程序实现后, 对于之前的公共组件应如何处理?

范怀宇:小程序还是一个比较独立的技术体系,现在已经有很多第三方的人在做一些框架(比如https://zhuanlan.zhihu.com/p/24176267),把一些现实中比较好的 Node.js 库和方法导入进来。但我自己的观点是小程序还太早期,变化可能会比较多,还是尽可能当作一个单独的技术线来处理,不需要太考虑复用。

淀粉提问:在小程序后台切换前台后,如何控制进入到指定页面?

范怀宇:很遗憾,还没有办法,这个事情还蛮奇怪的,感觉微信还是有点保守。小程序仅支持本页刷新和打开新页面两种模式,连从一个 tab 跳到另一个 tab 都不行。不过,如果用户推出再进入小程序,小程序会记住上次的位置,这个可以利用一下。

淀粉提问:您好,请问个人如何尝试小程序?(如何注册开发者账号?),我是一个有一年前端开发经验的菜鸟,在涉猎小程序之前需要做什么基本功的预备训练?(JavaScript?ReactJS?NodeJS?)谢谢。

范怀宇:小程序还不对个人开放注册,不过,用本地模式也是可以开发程序的,只是无法提交。小程序的技术栈用了 HTML/CSS/JavaScript,对于一个有前端经验的人而言,语言关不难。目前最好的学习资料,还是官方的 demo 和文档了,其它就只能靠自己摸索。我个人觉得,现在开发小程序,主要的成本确实在于平台不够稳定,需要一定的经验。

淀粉提问:贵公司开发小程序的流程是什么,前后端如何配合?前端需要维护几套?

范怀宇:我们公司正好,全部的 APIs 都是 HTTPS + JSON 的,所以后端几乎复用老业务就好了。流程和一般项目无异,GitHub 管理代码,协作开发。前端我们是没有复用现在的业务,就是全新写了一套。

淀粉提问:小程序内外部链接如何跳转?

范怀宇:小程序目前就是一个黑洞,无法外链跳出,我觉得这是微信想防止平台早期小程序之间互相导流做出来的限制。我们的解决方案就想前面分享中贴的那个图,我们自己做了一个中间服务来对 HTML 页面进行转义,然后在小程序中做了一个简版的 Web View,来渲染一个 HTML 的页面。

淀粉提问:小程序到底原理是什么?我可以这么理解么,微信小程序存在一个类似“编译”转换的工具,这么做是因为大多数人熟悉Web,你用Web语言描述小程序,然后微信把你描述好的Web语言转换成微信的Native“语言”?

范怀宇:当我知道小程序竟然几乎完整支持 CSS 的时候,我就明白,小程序的很大部分就是一个 “Web 中间语言”,把改造了的 HTML/CSS 转义成标准的 HTML 再渲染,最后的呈现主要还是依赖 Web 引擎。当然,它把部分组件,比如 video、map 这样的 Native 化了(引入了不少 Bug),来提升这些控件的性能。

淀粉提问:怀宇老师,想学习前端工程搭建,感觉好复杂。要用到很多工具和技术。请问学习过程中要注意什么?

范怀宇:前端的工具库缺失庞杂,建议,选一套先用就好,不一定需要先全部都尝试。而小程序其实工具就比较简陋了,倒其实是学习 Web 前端技术的开始。

淀粉提问:小程序和React Native相比,性能如何?

范怀宇:微信官方有个公开课,介绍了相关的部分 知乎专栏 。和 React 相比,小程序采取了一种非实时绑定,计算 diff 来更新的策略,和 React 比较只能说是各有利弊。它在最近的版本增加了 wx:key 这个机制,来改进性能。但可以想象,如果 diff 太大,它的性能优化是老大难,所以控制 setData 的粒度,是改进性能的一个方式。

淀粉提问:老师您好,在小程序中无法使用requestAnimationFrame方法,那在实现自定义动画时,如何规避setData改变style属性而带来的掉帧呢?

范怀宇:坦白讲,我也还没发现有太好的办法。小程序也有 animation 相关的 APIs,但做拖动这样的操作的时候,丢帧还是非常明显(而且不同平台表现不一致,这个也很痛苦)。短期内,还是不要做过于复杂的交互为妙。

淀粉提问:小程序用起来和普通APP体验有什么差别?

范怀宇:乍一看,没太大差别,不过仔细用来,性能上差距还是蛮明显的。尤其是涉及到动画这样比较细致的交互,尤为明显。长期使用,功能集上的差别也会比较凸显,小程序还是有不少做不了的事情(比如,通知、Web 页,等等)。但如果是不重度使用,是差不太多的。

淀粉提问:您认为小程序的出现会对当前的 APP 开发有什么影响?

范怀宇:小程序的出现,其实算是多了一个选择,原来大家需要抉择,我先做 iOS 还是 Android,或者就做个公众号,现在可能就需要考虑,要不我先做个小程序试试看?

淀粉提问:Vue 和小程序看起来有些类似,能简单比较下,介绍下两者的学习曲线吗?

范怀宇:两者确实有些点是相似的,都可以看成是一套独立的框架。相比而言,我觉得小程序的编程模式理解起来更简单一些,但不如 Vue.js 来的灵活。




1
收藏