微信小程序使用小结

大概是一年前,做了第一个微信小程序。那时候小程序刚起步,很多功能也都不太完善,使用起来很鸡肋。经过这么长时间,微信小程序的数量达到100多万,对开发者的开发体验也好(la)了(ji)蛮(yi)多(jiu)。最近又做了些微信小程序,有必要重新认识下。

1、小程序整体架构

下面是来自网上的一张图片,显示了微信小程序的整个框架和交互过程。

img

从上面的架构可以看到,整个小程序分成两个模块独立运行,一个是View层,还有一个Service层。View层用于前端UI展示,Service层控制后台逻辑,然后通过JSBridge进行交互。这就解释了为什么在小程序里面js不能直接使用浏览器的DOM和BOM接口。一个典型的交互流程如下:在View层,用户操作触发event,通过JSBridge通知到Service层,Service层处理完逻辑,把处理后的data再通过JSBridge传给View层,View层更新。

2、异步流程控制

在js开发里少不了异步的操作,小程序里也不例外。目前异步编程中,主要有以下几种解决方案:

  • 事件发布/订阅模式

  • Promise 模式

  • 一些异步流程控制库Q.js、Async、Bluebird等

    好在小程序已经提供了Promise的支持,避免回调地狱非常容易。下面是用小程序提供的api来发起网络请求:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    wx.request({
    url:
    method:
    success: () => {
    // 另一个异步请求
    wx.request({
    ...
    success: () => {
    // 再有另一个异步请求
    }
    })
    }
    fail:
    })

    这样的代码不太好,我们可以使用一个函数来封装一下api,使它支持promise。

    1
    2
    3
    4
    5
    6
    7
    function promisify(api) {
    return (options, ...params) => {
    return new Promise((resolve, reject) => {
    api(Object.assign({}, options, {success: resolve, fail: reject}), ...params)
    })
    }
    }

    再使用wx.request时可以像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    request = promisify(wx.request)
    request({
    url:
    method:
    }).then(() => {
    // 异步成功
    }).catch(() => {
    // 异步失败
    })

    download = promisify(wx.download)
    ...

    这样是不是简洁清晰很多。当然,有了Promise,并行异步任务,流程异步依赖的问题也可以很好处理。

3、页面间通信

小程序已经提供了组件功能,组件间的通信可以通过事件派发。但是在页面之间的通信却没有提供对应的机制。有这样一个需求,在首页显示了用户的钱包金额,到下单页下单之后,用户金额减少。要想在下单后首页的金额数发生变化,可以在首页的onShow事件中重新获取数据,这种处理显然不够妥。

更好的处理方式是建立一个全局的事件中心,所有页面间的通信通过事件中心统一管理。下面是一个简单的事件中心,使用事件发布/订阅方式处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Event {
constructor() {
this._queue = {}
}
on(key, callback) {
if (Array.isArray(key)) {
key.map(item => {
this._queue[item] = this._queue[item] || []
this._queue[item].push(callback)
})
} else {
this._queue[key] = this._queue[key] || []
this._queue[key].push(callback)
}
return this
}
off(key, callback) {
if (this._queue[key]) {
const index = typeof callback === undefined ? -2 : this._queue[key].indexOf(callback)
if (index === -2) {
delete this._queue[key]
} else if (index !== -1) {
this._queue[key].splice(index, 1)
}
if (this._queue[key] && this._queue[key].length === 0) delete this._queue[key]
}
return this
}
has(key) {
return !!this._queue[key]
}
emit(key, ...args) {
if (this._queue[key]) {
this._queue[key].forEach(callback => callback.apply(this, args))
}
return this
}
}

把事件中心注入到App内,通过在页面getApp(),可以获得全局的事件控制。

4、小程序数据埋点

预置的数据埋点是在各种App,Page事件上进行跟踪,发送所需数据给后台。下面这张图是Page实例的生命周期:

image

现在需要在onHide事件中发送这个事件并把相应的数据传给后台。容易想到的是直接在Page onHide事件里发送,但是这样代码没有跟业务解耦,也难在其它项目中复用。有没有更好的方式?

我们可以对Page方法进行重写,满足既执行埋点代码,也执行业务代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const pageHide = () => {
// 这里是埋点代码
}

// page/index/index
Page({
onHide: () => {
// 里面是业务代码
}
})

// 对Page重写
const copyPage = page
page = function(obj) {
copyPage(proxy(obj, { onHide: pageHide }))
}

// proxy函数返回新的obj
function proxy(obj, events) {
for (let key in events) {
if (typeof obj[key] === 'function') {
const fn = obj[key]
obj[key] = function() {
fn.call(this)
events[key].call(this)
}
} else {
obj[key] = function() {
events[key].call(this)
}
}
}
return obj
}

这样只需在App.js里引入埋点代码即可。

5、小程序开发框架

在小程序还不支持组件化、Promise、箭头函数的时候,这是很痛苦的,这时候一些框架应运而生,解决掉了这些痛点。比较流弊的框架有Wepy(腾讯团队)、mpvue(美团团队)、Taro(京东凹凸)。Wepy是比较早出现的小程序开发框架,采用类vue风格语法,但是官方又推荐redux做状态管理,应该是吸收了vue和react的优点,大家都说坑太多;mpvue fork自vue,对runtime 和compiler部分进行了修改,vuex做状态管理,各技术栈一致,思想统一,有问题好解决;Taro号称是多端统一开发框架,支持用 React 的开发方式编写一次代码,生成能运行在微信小程序、H5、React Native 等的应用,但是,现实有些差距。

综合一下,以后用mpvue来开发不错,vue 的组件在H5和小程序公用,支持npm包管理,各种工程化开发方式解锁。


微信小程序使用小结
https://keminu.github.io/2018/08/25/微信小程序使用小结/
作者
xiaoka
发布于
2018年8月25日
许可协议