不懂函数式?用 mobx 来写 react 应用吧

Posted SegmentFault

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不懂函数式?用 mobx 来写 react 应用吧相关的知识,希望对你有一定的参考价值。

目的

直接了当吧:其实这篇就是想安利大家一个新的状态管理库。如果你使用 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,大体上,步骤分为两步:

  1. 写模型的 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 没啥区别。

这里多了个 @observabledecorator目前在 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 testedtransparently 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,相对高级点,比如

  1. @computed: 由最开始的 state 生成出来的 state,比如 fullName是由 firstName 和 lastName 生成的,如果 firstName 改变,fullName 会重计算。监听 @computed 属性,和监听 @observable 属性,在使用上是一样的。理论上,可以写无数多层这样的依赖。

  2. untrack:在某一时刻使用了 state 的某个属性,但是不想对这个 state 属性产生依赖

  3. transaction:在transaction块中执行的属性修改,只会在块结束时,触发一次Derivations的变更或执行。这就避免了不必要的多次副作用,比如多次 render react 组件。

  4. useStrict: 非严格模式下的 mobx,任何地方都可以修改 state,这样很快就会让 state 的管理难以维护。严格模式下,只有标记了@action的函数或在runInAction中的代码,才能修改 state。这个强烈建议使用

  5. spy & intercept 做单个 state 或全局所有 state 的拦截。这给 log 等功能,提供了很好的便利。

还有一些,大家去读文档吧。

题外话

model 改变,ui 自动变化,mobx 写着,让我很有 vue 的感觉。感觉就是多了几个decorator.

缺点

社区还不够丰富。mobx 的资料还不多,基本没有中文资料。我安利的同学,都反映官方的 get start(英文)看着有点蛋疼。然后我说,想想你们写 redux 的第一个 demo,然后他们表示刚才的疼不算什么~。

相信大家手上已经有些项目用 redux 了,如果用着也算顺手,其实也就没必要换了。我本人也同时维护着使用 redux 的项目,然后在新项目里使用 mobx,我的效率是大幅提升的,但是老项目重写的代价太大。。。

结论

所以,如果你使用 react,更熟悉面向对象,羡慕 vue 的简单直观,真心安利你,体验下 mobx。写的很舒爽,而且程序的性能有保证。对了官方还提供了一个 dev-tools,非常不错,能够直接看到哪些组件被重渲染了。

官方还提供了一个 starter,可以省去各种配置。

-EOF-



【活动推荐】又拍云 Open Talk NO.24 沙龙,主题为「Docker 与微服务架构实践」,分享议题包括:

  • 邱戈川 -《唯品会“简易” DCOS 实践探讨》

  • 叶 靖 -《基于 Docker 云处理服务架构实践》

  • 敖小剑 -《PPMoney 微服务架构之路》

对主题感兴趣的小伙伴,可通过点击「阅读原文」报名活动。

以上是关于不懂函数式?用 mobx 来写 react 应用吧的主要内容,如果未能解决你的问题,请参考以下文章

Mobx6React + Typescript 实践

用Inferno代替React开发高性能响应式WEB应用

mobx+react基础

mobx基本概念

React Hooks Mobx:无效的钩子调用。 Hooks 只能在函数组件的主体内部调用

React函数组件如何集成mobx