前言
在之前文章里可以知道,本人一直致力于推广react和mobx结合开发webapp。很遗憾的是个人目前为止还没在正式的react项目中使用过mobx,但最近一个偶然的机会,终于让我有机会尝试一下mobx了,不过是在小程序的项目里。
简述
本文主要讲述mobx在小程序里如何使用的原理,如果不懂mobx的话可以试着参考一下简书上的文章和官方文档。
正文
-
了解小程序的一些工作原理。
工作原理。通过官方文档我们了解到,在小程序里,渲染页面(webview)和js的逻辑(JSCore)是各自两个不同的环境,类似于前端和后端。js里的数据(data) 传送到页面是依赖于一个特定的通信机制的,并且它是以字符串的形式传递的。
也就是说,使用mobx创建的数据对象(observable state),必须通过 setData 方法来传给页面。而且传输的时候会转成字符串。这意味着你的对象会被拷贝一次。但是如果你直接把 observable state 传给页面是有一定的坑的。例如,对象里的属性是在 __proto__ 上,或者是不可遍历属性的话,wxml 里会拿不到这些数据。原因就是上面所说的,对象转字符串是一个遍历拷贝的操作。会不会出现这种情况取决于你创建对象是通过对象直接量还是通过 new Class 的形式。
解决这问题最好的办法是先手动 深度拷贝 一遍 observable state,再去 setData,关于这一点等一下再详细说。
-
怎么让 wxml 响应 observable state 的变化
mobx.autorun
这个方法接受一个回调函数作为参数,它会自动去调用一次回调函数。
当回调函数里使用的 observable state 发生变化时,这个回调函数又会再次被 autorun 再执行一遍。因此,只要把 observable state 的使用和 setData 放在autorun 里,就可以自动的更新视图了。但有一个问题,在 autorun 里要使用哪些 observable state?还有怎么去使用 observable state。毕竟 observable state 的作用是用于 view 层的。但小程序不像 react 那样可以在 render 函数里渲染 view, 小程序是采用模板语法的。解决这个问题,比较简单的方法是,所有 observable state 都去使用一遍,这样就能让 autorun 监测到所有 observable state。使用的方法也很简单,深度拷贝 一遍。
-
深度拷贝
综上所说, 深度拷贝是使用 mobx 的关键
以一个简单的例子说明一下,深度拷贝会遇到的问题。class UserStore { @observable user_name = 'Darko Kukovec' @computed get last_name() { return this.user_name.split(' ').pop() } }
被 observable 包装的实例属性会在 __proto__ 上,而不是实例的直接属性。但如果在初始化方法 constructor 里使用 extendObservable 的话,上面所说的属性会被复制到实例的直接属性上。
被 computed 包装的 getter 拦截器。在 class 上声明的 getter 拦截器本来就会放在 prototype 上,并且是不可遍历。这一点对于 computed 也不例外。
Object.getOwnPropertyNames。这个方法能拿出对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)。但要注意,Object.getOwnPropertyNames(obj) 不能拿到 obj.__proto__(原型链)上的属性。需要通过 Object.getOwnPropertyNames(obj.__proto__)获取。
for in 循环能遍历对象的所有可遍历属性,包括 __proto__ 上的属性。
mobx.toJS。 mobx提供的深度拷贝方法,但是计算值和其他不可枚举的属性不会成为结果的一部分。
以上几点是在做 observable 对象的深度拷贝要注意的。
miniapp-mobx-starter-kit
至于深度拷贝具体怎么做,怎么把 mobx 跟 小程序结合起来?可以参考一下我自己构建的小程序开发的脚手架 miniapp-mobx-starter-kit。主要逻辑在 src/store/helper/observer.js 里。