目的
直接了当吧:其实这篇就是想安利大家一个新的状态管理库。如果你使用react,更熟悉面向对象,羡慕vue的简单直观,对redux感觉有些烦躁,真心安利你,体验下mobx
.
安利的同时,略带些内容。内容走起
内容
因为看到redux
的作者,在twitter推荐了mobx
这个库:
unhappy with redux? try mobx
大神主动推荐自己成名作品的替代品?!!!
于是立马尝试。不到一个下午的时间,基本上手,然后晚上回去想了想代码组织的问题,试用了起来。发现开发效率大幅提升,而且整个工程的脉络很清楚。
然后回想起当初学redux,真感觉自己智商不够。看了一周文档,理念理解的七七八八,不知道如何写代码;然后因为源码不多,又看了一周redux的源码,还发现了个bug,提了pr,但是依旧不知道如何写代码。。最后只能硬着头皮无脑抄别人的代码,才大致明白了整个流程。
所以从自身实际情况的对比,有段时间略傻x的变成了redux无脑黑,并在很多场合说过,谁用redux谁加班。。。现在想想too simple
经过不断的讨论,觉得使用redux不爽,最大的原因是不熟悉函数式,而且js本身也没有在语法上为函数式提供什么便利。
redux更像是定了一种规范,然后为了能把这种规范应用到工程中,提供了几个帮助函数。如果对函数式很熟悉,那么会非常认同这套并很舒服的使用起来。
但是,熟悉函数式的工程师数量远比熟悉面向对象的少,我对函数式也就只了解个皮毛。。
言归正传,来看看mobx
吧.
使用mobx的写react,大体上,步骤分为两步:
写模型的class
import $ from 'jquery' // just for ajax usage
import { observable } from 'mobx'
class Posts{
@observable list=[]
//这里为了演示,直接$.ajax。
//但是这样的model,很难测试!!
fetchPosts(){
$.ajax({
url:'/posts'
}).done((data)=>{
this.list = data.postList
})
}
}
这个对于熟悉OOP的人,亲切的不行,写model类这事,有点经验的工程师,没写个上千个,也写个几百个吧。异步的处理,也很直白,请求,然后根据请求的数据,处理相应的属性。这段代码,如果不引入es6的语法,和以前jquery时代,写model没啥区别。
这里多了个 @observable
。decorator
目前在es7里还没定稿,通过babel 6
使用也需要通过引入一个plugin,这里使用,能可以提高可读性。当然不使用decorator
也可以,原理上,就是给list
包了个函数,这个函数会对list
进行处理,让对list的读写拥有了pub/sub
的功能。
当然mobx
提供的功能要比这多很多,根据官方描述
MobX is a battle tested library that makes state management simple and scalable by transparently applying functional reactive programming (TFRP)
大家注意两点 battle tested
,transparently applying functional reactive programming
mobx
做了大量的工作,能让你只关注操作对象属性即可,而背后的理念,就一句话
Anything that can be derived from the application state, should be derived. Automatically.
应用的状态是本源,其他的部分,都应该从本源导出(derived)。比如上面的Posts类,本源就是 @observable list
.然后其他部分,依赖这个list去做一些事情,比如 mobx
提供了autorun
函数
//写在 class Posts里
autorun(()=>console.log(this.list.length))
autorun里的匿名函数对list进行了取值,mobx
知道,这个副作用函数,依赖list,所以list有变动,都会执行这个函数。这个函数的运行,是list
这个应用状态驱动的。如果应用还有其他状态,mobx
不会触发这个函数,因为这个函数只依赖 list
.
使用react后,ui的变化也是状态变化后,产生的副作用。所以很自然
autorun(()=> redner(this.list))
那么状态的变化,就引起ui的变化,而且仅当this.list
变化,才会引起ui的变化,其他应用状态的变化,不会产生影响,这也是mobx
不需特别性能优化的原因,只有相关状态变动,才会影响ui。实际上,在只render需要render的组件
方面,官方出的mobx-react
做了更多的工作。
2.写react组件
有了上面的class Posts
,我们有个组件要展示post list
import React from 'react'
import { observer } from 'mobx-react'
import Posts from './Posts'
@observer
class PostListComp extends React.component {
contructor() {
this.posts = new Posts();
this.posts.fetchPosts()
}
render() {
return (
<ul>
{this.posts.list.map((post)=>(
<li key={post.id}>post.title</li>
))}
</ul>
)
}
}
这里Posts实例
不打算全局使用,所以赋值在 this.posts上。开始的时候,this.posts.list
是[],这ok,ul里是空的,fetchPosts
中异步请求结束,this.posts.list
就有了从服务器取来的数据,这时mobx知道,这个react组件依赖于this.post.list
,那渲染吧,就这样。
这里的状态是PostListComp
组件自己new处理的,不和其他组件共享;如果想共享,可以放在两个组件的共同祖先那实例化,然后再各自传入,甚至根据业务,可以做成全局的单例。想想你处理对象的技巧,都能用在这里,如果你想用DI来管理这些对象,也可以,有文章介绍了mobx结合InversifyJS
使用的情况。
如果posts
是父组件传给PostListComp
的,如果父组件的渲染不需要posts.list
,posts.list
的变化只会重渲染 PostListComp
,而不会重渲染父组件。只render需要render的组件
,所以性能优越。甚至可以做到下面这样(先不要纠结下面代码的一些细节)
const profileView = observer(props => {
if (props.person.nickName)
return <div>{props.person.nickName}</div>
else
return <div>{props.person.fullName}</div>
});
如果nickName不为空,那么mobx知道,这个view只依赖于nickName,fullName的变动不会引起view的变化,从而不会从新render组件。只render需要render的组件
(这话我已经说了3遍了!)
其他的api
mobx
还提供了其他一些api,相对高级点,比如
@computed
: 由最开始的state生成出来的state,比如fullName
是由firstName和lastName生成的,如果firstName改变,fullName会重计算。监听@computed属性,和监听@observable属性,在使用上是一样的。理论上,可以写无数多层这样的依赖。untrack
:在某一时刻使用了state的某个属性,但是不想对这个state属性产生依赖transaction
:在transaction
块中执行的属性修改,只会在块结束时,触发一次Derivations
的变更或执行。这就避免了不必要的多次副作用,比如多次 render react组件。useStrict
: 非严格模式下的mobx,任何地方都可以修改state,这样很快就会让state的管理难以维护。严格模式下,只有标记了@action
的函数或在runInAction
中的代码,才能修改state。这个强烈建议使用spy & intercept 做单个state或全局所有state的拦截。这给log等功能,提供了很好的便利。
还有一些,大家去读文档吧。
题外话
model改变,ui自动变化,mobx写着,让我很有vue的感觉。感觉就是多了几个decorator
.
缺点
社区还不够丰富。mobx的资料还不多,基本没有中文资料。我安利的同学,都反映官方的get start(英文)看着有点蛋疼。然后我说,想想你们写redux的第一个demo,然后他们表示刚才的疼不算什么~。
相信大家手上已经有些项目用redux了,如果用着也算顺手,其实也就没必要换了。我本人也同时维护着使用redux的项目,然后在新项目里使用mobx,我的效率是大幅提升的,但是老项目重写的代价太大。。。
结论
所以,如果你使用react,更熟悉面向对象,羡慕vue的简单直观,真心安利你,体验下mobx。写的很舒爽,而且程序的性能有保证。对了官方还提供了一个dev-tools,非常不错,能够直接看到哪些组件被重渲染了。
如果你被安利成功,想看看mobx,建议从官方文档开始,或者去官方的推荐列表里,找找视频教程(不要想,肯定是英文的),地址是 http://mobxjs.github.io/mobx/faq/blogs.html
官方还提供了一个starter,可以省去各种配置。