微信小程序开发之尝试 UI 逻辑分离
在大概 8 月底,有幸参与了企鹅 FM 和微云的微信小程序开发,这篇文章是我对 UI 逻辑分离的思考总结,另由于微云的业务逻辑代码实在太复杂勒……所以文章中将主要以 FM 为例。 UI 分离 在微云和企鹅 FM 项目中我们都是采用 UI 工程师+前台工程师的模式,所以必然出现了我们(总是吐槽的)在日常页面开发中会采用的方式: [图片] 在 html/wxml 结构中用注释的方式,告诉开发 GG:“当出现 XXX 情况的时候,加上 YYY class”,于是业务逻辑代码中,就一定会掺杂着 UI 逻辑,甚至 hard code 的地方: [图片] FM 中不止一种场景会触发“播放”/”暂停”逻辑的应用,也就是有多种可能会触发 UI 的变化,那么 UI 逻辑还会有重复的地方。万一有一天需要更换或新增或删除 class 名,就很有可能出错。 如果可以把 UI 逻辑独立处理就好了,这是当时我的想法。经过合作的开发 GG 提点之后,由于很多 UI 层的逻辑是跟着业务逻辑走的,所以完全剥离 UI 逻辑是不现实的。强行分离就需要把[代码]this[代码] 传来传去,在我看来也不是回事儿。所以 UI 逻辑采用的还是单纯的“变量分离”,可以粗暴理解为,把当时写在注释里的内容,写到独立的 js 文件中。 下面以 FM 为例,来看看我是怎么做的吧~ FM 中 UI 会出现变化的是以下几种场景: [图片] 播放器有两种显示模式:mini 播放器和全屏播放器 这两种模式是通过在播放器上切换 [代码].mini[代码] class(mini 状态需要 [代码].mini[代码] )实现 全屏播放器的播放按钮有“播放”和“暂停”两种状态(图片)切换 因为小程序不支持 [代码]background-image[代码] ,所有图片需要通过 [代码]<image>[代码] 组件现实,图片的切换可以通过换不同的 [代码]src[代码] 值实现。 当播放器进入全屏模式后,节目列表将被隐藏;而到 mini 播放器时,节目列表将重新显示出来 列表中的节目,播放按钮有“播放”和“暂停”两种状态切换 同 2,通过切换 [代码]src[代码] 值实现(这里应该也可以用 [代码]wx:if[代码] 来实现)。 项目结构如下,其中在 [代码]utils[代码] 目录中的 [代码]view.js[代码] 是 UI 逻辑部分的代码: [图片] [代码]pages[代码] 目录中的 js 文件将通过 [代码]require[代码] 引用 [代码]view.js[代码],[代码]view.js[代码] 中的接口分为“通用”和“页面使用”这两个类型。 [代码]module.exports = {[代码][代码] [代码][代码]// 通用[代码][代码] [代码][代码]general: {[代码][代码] [代码][代码]hide: [代码][代码]'hide'[代码][代码],[代码][代码] [代码][代码]show: [代码][代码]'show'[代码][代码],[代码][代码] [代码][代码]getScreenHeight: getScreenHeight[代码][代码] [代码][代码]},[代码][代码] [代码][代码]// 播放器页面[代码][代码] [代码][代码]playerView : {[代码][代码] [代码][代码]class: {[代码][代码] [代码][代码]mini: [代码][代码]'mini'[代码][代码], [代码][代码]// 小播放器模式要有这个 class[代码][代码] [代码][代码]listItemPlaying: [代码][代码]'playing'[代码][代码] [代码][代码]},[代码][代码] [代码][代码]images: {[代码][代码] [代码][代码]listPlayBtn: [代码][代码]'../../images/play/play-list.png'[代码][代码],[代码][代码] [代码][代码]listPauseBtn: [代码][代码]'../../images/play/pause-list.png'[代码][代码],[代码][代码] [代码][代码]playerPlayBtn: [代码][代码]'../../images/play/play-player.png'[代码][代码],[代码][代码] [代码][代码]playerPauseBtn: [代码][代码]'../../images/play/pause-player.png'[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]// 其他页面如果也有需要,以页面为单位添加...[代码][代码]}[代码] 在上面的代码中,可以看到 [代码]general[代码] 里暴露了一个 [代码]getScreenHeight[代码] 方法,它用于获取屏幕高度,我们在 [代码]onLoad[代码] 的时候通过它,将设置了背景色的结构的 [代码]min-height[代码] 属性值设为屏幕高。用 js 的原因是,开发者工具 0.10 把 [代码]<page>[代码] 的高度 100% 去掉了,所以在 wxss 中就不能设置 [代码]height: 100%[代码] 把屏幕高度继承下来,但我们又要保证在页面资源加载出来以前,用户看到的就是全屏的暗色背景。 在页面使用的接口里就分成了 [代码]class[代码] 和 [代码]images[代码],如果未来出现更多 UI 变化的场景,可以再通过变量添加上去,比如 [代码]pageView.id[代码]。 举个超级简单的例子(如下),模拟工作流程: [图片] 在 wxss 中定义好控制不同样式的 class 将需要变化的 class 写到 [代码]view.js[代码] 中,并暴露接口 在 wxml 中的对应结构中绑定 event handler 在对应的 [代码]page.js[代码] 里实现 event handler 的具体内容,也就是切换 class 的触发条件 [图片] 结论 老司机一看就知道是 MVVM 模式。 这样分离也就是为了 UI 有独立的控制器,不至于和业务逻辑耦合严重,在页面开发的阶段就可以完成 UI 上的变化。之前写网页的时候,当需要 UI 变化的时候,我们常常会在页面上绑定事件,示意前台GG 逻辑是怎么样的,但是那部分代码可能并不能直接放入他们的业务逻辑中(可能是规范、逻辑没有考虑完整……原因)。从这个角度上看,小程序反而能给 UI 工程师更多控制 UI 逻辑的能力,确定好代码规范和接口,也能方便前台 GG 直接使用 UI 代码,专心业务逻辑~