专栏首页腾讯IVWEB团队的专栏mobx 能为我们带来哪些改变

mobx 能为我们带来哪些改变

介绍

第一印象

首先上一段代码:

import {observable, observe} from 'mobx'
var student = observable({  
    name: 'unknown'
})
observe(student, (change)=>{  
    console.info(change)   
    /*  will print:
    * { type: 'update',
    * object: { name: [Getter/Setter] },
    * oldValue: 'unknown',
    * name: 'name',
    * newValue: 'foo' }
    */
})

student.name = 'foo'

非常直观的,student的属性改变了,监听的方法打印出了所有的变动细节。

简介

mobx是一个状态管理器,用于TFRP(transparently applying functional reactive programming)自发式函数相应式编程。

可以看到,它的核心概念包括action->state->computed value -> reaction

通过action数据(state), 然后state引发的变动触发computed-value变动,最终传给监听器(reaction),如果把react的的render方法也注册为监听器,那么mobx就可以和react配合起来构建一个健全的app架构了。

基本使用

mobx的原理是在对象包裹setter和getter。例如当我们调用一个属性时候:obj.name就会触发name的getter,调用obj.name = 'hello world'的时候,就会触发name属性的setter。第一小节的例子如果自己写,大致是这个样子的:

var student = {
  _name: 'unknown',
  get name(){
      console.info('getter of student.name')
      return this._name;
  },
  set name(val){
      console.info('setter of student.name')
      this._name = val;
  }
}

student.name = 'richcao'  //=> setter of student.name
student.name //=> getter of student.name

特性和局限

很难单独的说mobx带来哪些特性和哪些局限,这里都是结合react,以实际工程使用角度来考量mobx的优劣。

特性

属性变动精确定位

这点特性联系到页面重绘:

var student = observable({
    name: 'richcao',
    age: 28
})

ReactDom.render(
	mobx.observer(()=>(
    	<div>{student.name}<div>
    )),
	document.getElementById('root')
);


student.name = 'foo'  //会引发重绘
student.age = 18  // 不会引发重绘

如果要用redux实现这点,估计需要不计其数的shouldComponentUpdate方法了吧。

视图层缓存

mobx提供了一个computed方法,通过computed方法计算的值如果放到了监听器中,那么这个值会被缓存,state没有变动的情况下,computed的值不会重新计算。

想想我们的react中的render方法, 在方法中创建的中间变量都会被重新计算,例如下面的代码,每render一次,就需要计算一次totalPrice,简单的场景还好,如果计算量偏大,这样的消耗就不该发生。

render(){
    let totalPrice = this.props.price * this.props.num;
    return <div>{totalPirce}</div>
}

可不可以把totalPrice缓存起来,只要props.price和pros.num没有变化,就不用重新计算totalPrice?答案是可以的。mobx提供了一个computed方法,用于将state映射为新的值,这些值常常被用作视图层的渲染。看下面的例子:

const a = observable(1);
const b = observable(2);

const sum = computed(() => {
  console.log("computing");
  return a.get() + b.get();
});

sum.get() // outputs "computing", returns result
sum.get() // outputs "computing", returns result
sum.get() // outputs "computing", returns result

// create an observer (reaction/autorun/render)
const disposeObserver = autorun(() => sum.get()) // outputs "computing", result is cached

sum.get() // outputs nothing, cached value returned
sum.get() // outputs nothing, cached value returned
sum.get() // outputs nothing, cached value returned

// dispose observer
disposeObserver()

sum.get() // outputs "computing", returns result
sum.get() // outputs "computing", returns result
sum.get() // outputs "computing", returns result
}

局限

使用mobx需要妥协的有:

1、内存上的增加:

mobx会将给定对象深拷贝一份作为私有变量,然后再创建一个对象,包含该私有变量所有属性的getter和setter方法。无疑在内存占用上,比单纯的使用plain javascript object占用更多的内存。例如:

var student = {name: 'foo'}
var observableStudent = observable(student);

mobx会深拷贝student,然后创建一个{ name: [Getter/Setter] }返回。实际上就“多了”两个student的副本。

2、所有需要监听的变量都必须挂载到一个上级对象上。

单一的变量无法绑定getter和setter,所以,只有ObjectArrayMap才能够被包裹成observable。除此之外,字面量、函数、有prototype的类等都不能监听变化,看下面的代码:

var age = observable(23);
mobx.observe(age, console.info)
age = 18;   // nothing happened

关于原生变量的情况,mobx返回的是一个observable.box对象,可以去官方文档中查看。其实这一点我们注意避免即可。

3、全局state维持同一对象引用

state维持为同一个对象的引用,这样要做状态回滚(撤销操作)就比较困难了。

我们知道redux的每次store更新,都生成一个新的state引用,可以把旧的state保存下来,需要撤销操作时,将旧的state重新render一遍就好了。

这个特性放到mobx上就不太可能了,因为我们压根儿没有Object.assign一个新的state出来。好在,mobx团队也给出了解决方案:mobx-state-tree。其底层和immutable一样,是一个不可变类型的数据容器,但是在api上比immutablejs容易使用太多太多。我在另一篇文章中讲吧.....

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • CLI子命令扩展-插件机制实现

    开发 CLI 工具过程中,为了便于扩展,将 CLI 的实现分为基础功能和扩展功能。基础功能包括init、build、lint、publish 等伴随工程从初始化...

    腾讯IVWEB团队
  • 为元素添加边框,你有多少种好办法?

    相信大家在做项目的过程中会经常遇到这样的一个需求,鼠标 hover 到某个元素的时候为这个元素添加边框,这个简单的需求仅需注意添加边框后不会对原有的位置造成布局...

    腾讯IVWEB团队
  • 如何搭建高质量、高效率的前端工程体系--页面结构继承

    本系列文章将会将会围绕整个前端的开发流程出发解决这两个问题,帮助大家提高产品质量以及开发效率。本文介绍页面利用类似的继承方式以便达到页面结构以及数据重用。

    腾讯IVWEB团队
  • 我的python学习--第七、八天

    HTTP(与web应用会话的协议)有许多不同的URL方法。默认情况下,路由只回应GET请求,但是通过route()装饰器传递methods参数可以改变这个行为。

    py3study
  • Spring 学习笔记(二)—— IOC 容器(BeanFactory)

      使用Spring IoC容器后,容器会自动对被管理对象进行初始化并完成对象之间的依赖关系的维护,在被管理对象中无须调用Spring的API。

    Rekent
  • mybatis系列教程(一)——mybatis配置

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    逝兮诚
  • 10 . Kubernetes之Configmap,Secret

    Configmap和Secret类似,用来存储配置文件的Kubernetes资源对象,所有的配置内容都存储在etcd中.

    常见_youmen
  • XLNet详解

    2018 年,谷歌发布了基于双向 Transformer 的大规模预训练语言模型BERT,刷新了 11 项 NLP 任务的最优性能记录,为 NLP 领域带来了极...

    mathor
  • LVS工作总结之原理篇–DR模式

    老七Linux
  • StreamingPro 基于Spark 2.1.1版本 支持Spark Streaming

    很多人吐槽StreamingPro构建实在太麻烦了。看源码都难。然后花了一天时间做了比较大重构,这次只依赖于ServiceFramework项目。具体构建方式如...

    用户2936994

扫码关注云+社区

领取腾讯云代金券