精华内容
下载资源
问答
  • mobx

    2019-10-29 14:25:15
    Mobx提供了一个mobx-react包帮助开发者方便地在React中使用Mobxmobx-react中有observer、Provider、inject几个常用的api。在《mobx系列(二)-mobx主要概念》中我们已经介绍过observer,本文介绍下inject、Provider...

    Mobx提供了一个mobx-react包帮助开发者方便地在React中使用Mobx,mobx-react中有observer、Provider、inject几个常用的api。在《mobx系列(二)-mobx主要概念》中我们已经介绍过observer,本文介绍下inject、Provider,以及Mobx如何与React结合使用。

    1、Provider

    Provider是一个React组件,使用React的上下文(context)机制,可以用来向下传递stores,即把state传递给其子组件。

    例如,有如下形式的一个store:

    import {observable, computed, action} from 'mobx';
    class userStoreClass {
        @observable user = {
          name: 'admin',
          role: '管理员'
        };
        count = 0;
        @computed get userName(){
            return this.user.name;
        }
        @action changeUser(){
          if(this.count % 2 === 1){
              this.user = {
                  name: 'admin',
                  role: '管理员'
              };
          }else{
              this.user.name = 'guest';
              this.user.role = '访客';
              this.user.isGuest = 'true';
          }
          this.count ++;
        }
    }
    const userStore = new userStoreClass();
    export default userStore;
    

    使用Provider传递store的方式为:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {configure} from 'mobx';
    import {Provider} from 'mobx-react';
    import userStore from './models/userStore';
    import App from './components/App';
    // 状态始终需要通过动作来更新(实际上还包括创建)
    configure({'enforceActions': 'always'});
    ReactDOM.render((
        <Provider store={userStore}}>
            <App />
        </Provider>
      ), document.getElementById('container'));
    

    如果有多个store,可以使用类似于如下形式:

    const stores = {
      mainStore, userStore, commonStore
    };
    ReactDOM.render((
        <Provider {...stores}>
            <App />
        </Provider>
      ), document.getElementById('container'));
    

    2、@inject

    
    inject是一个高阶组件,作用是将组件连接到提供的stores。具体说是与Provider结合使用,从Provider提供给应用的state数据中选取所需数据,以props的形式传递给目标组件。用法为:
    
    inject(stores)(component)
    @inject(stores) class Component...
    对应上节的例子,App内使用的组件User使用@inject方式为:
    
    ```javascript
    import React, {Component} from 'react';
    import {inject, observer} from 'mobx-react';
    import {Button} from 'antd';
    import './style.css';
    @inject( 'userStore')
    @observer
    export default class User extends Component{
        constructor(props){
            super(props);
            this.state = {};
        }
        render(){
    	// 可以以this.props.userStore形式获取store内state
            const {user} = this.props.userStore;
    	// 以.形式使用对象的属性值
            return(
                <div className='user'>
                    <div className='user_list'>name:{user.name}</div>
                    <div className='user_list'>role:{user.name}</div>
                    <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
                    <Button type='primary' onClick={() => this.props.userStore.changeUser()}>Change User</Button>
                </div>
            );
        }
    }
    

    3、react-mobx实践示例

    我写了一个react-mobx的简单demo,地址为:https://github.com/smk108/mobx_demo ,

    demo的结构为:

    依据组件的划分使用了3个store,需要说明的是我是以class的形式创建的store,store中export的是store class的instance,例如第一节中userStore的形式:

    const userStore = new userStoreClass();
    export default userStore;
    在React中使用Mobx的方式有很多,Mobx不会强制要求以某种方式使用它,我在demo中使用的方式仅仅是其中一种。事实上,你可以以任何你喜欢并且能生效的方式使用它。在下一篇《Mobx定义数据存储》中会介绍、对比文档推荐使用的数据存储和我的demo中使用的数据结构间的不同。

    4、可观察的局部组件状态

    Mobx允许在响应式React组件内使用自由地使用状态,意味着我们可以将一些状态像普通React组件一样管理,例如对上面提到的demo中User组件做如下修改:

    import React, {Component} from 'react';
    import {inject, observer} from 'mobx-react';
    import {Button} from 'antd';
    import Timer from '../Timer';
    import './style.css';
     
    @inject( 'userStore')
    @observer
    export default class User extends Component{
        constructor(props){
            super(props);
            this.state = {
                userChangeTimes: 0
            };
        }
     
        handleChangeUser(){
            this.props.userStore.changeUser();
            let {userChangeTimes} = this.state;
            userChangeTimes ++ ;
            this.setState({userChangeTimes});
        }
     
        render(){
            const {user} = this.props.userStore;
            return(
                <div className='user'>
                    <div className='user_list'>name:{user.name}</div>
                    <div className='user_list'>role:{user.name}</div>
                    <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
                    <div>user change times: {this.state.userChangeTimes}</div>
                    <Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
                    <Timer />
                </div>
            );
        }
    }
    

    User组件state的userChangeTimes可以使用setState进行修改,一切都和不使用Mobx时一样。但是,推荐响应式组件应该没有或很少有状态,因为在与其他组件共享的对象中封装(视图)状态通常更方便。针对响应式组件需要维护单独状态的情况,Mobx为我们提供了更加方便的一种方式-可观察的局部组件状态。

    Mobx允许使用@observable在React组件内引入可观察属性,意味着我们不需要通过React 的冗长和强制性的 setState 机制也可以在组件中拥有功能同样强大的本地状态(local state)。

    用法如下(使用demo中Timer组件举例):

    import React, {Component} from 'react';
    import {inject, observer} from 'mobx-react';
    import {observable, action} from "mobx";
    import './style.css';
     
    @inject('commonStore')
    @observer
    export default class Timer extends Component{
        constructor(props){
            super(props);
            this.state = {};
        }
        @observable secondsPassed = 0;
     
        componentWillMount(){
            this.props.commonStore.startTime();
            this.timer = setInterval(this.handleChangeSecondsPassed,1000);
        }
     
        @action.bound handleChangeSecondsPassed(){
            this.secondsPassed ++;
        }
     
        render(){
            const {time} = this.props.commonStore;
            return(
                <div className='time_content'>
                    <div>{time}</div>
                    <div>Seconds passed:{this.secondsPassed}</div>
                </div>
            );
        }
    }
    

    secondsPassed作为组件内可观察的局部状态,不使用setState也触发UI的响应。

    需要注意的是:

    可观察局部状态会被render提取调用;
    可观察局部状态的修改会触发React的componentWillUpdate和componentDidUpdate生命周期,不会触发其它的生命周期;
    如果你需要使用React的其它生命周期方法,请使用基于state的常规React API;

    5、生命周期钩子

    当使用mobx-react时可以定义一个新的生命周期钩子函数componentWillReact,当组件因为它观察的状态发生改变时,组件会重新渲染,这时componentWillReact会触发,可以帮助追溯渲染并找到导致渲染的动作(action)。

    修改demo中User组件举例如下:

    import React, {Component} from 'react';
    import {inject, observer} from 'mobx-react';
    import {Button} from 'antd';
    import Timer from '../Timer';
    import './style.css';
     
    @inject( 'userStore')
    @observer
    export default class User extends Component{
        constructor(props){
            super(props);
            this.state = {
                userChangeTimes: 0
            };
        }
     
        handleChangeUser(){
            this.props.userStore.changeUser();
            let {userChangeTimes} = this.state;
            userChangeTimes ++ ;
            this.setState({userChangeTimes});
        }
     
        componentWillReact() {
            console.log("I will re-render, since the user has changed!");
        }
     
        render(){
            const {user} = this.props.userStore;
            return(
                <div className='user'>
                    <div className='user_list'>name:{user.name}</div>
                    <div className='user_list'>role:{user.name}</div>
                    <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
                    <div>user change times: {this.state.userChangeTimes}</div>
                    <Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
                    <Timer />
                </div>
            );
        }
    }
    

    需要注意的是:

    componentWillReact 不接收参数;
    componentWillReact 初始化渲染前不会触发 (使用 componentWillMount 替代);
    componentWillReact 对于 mobx-react@4+, 当接收新的 props 时并在 setState 调用后会触发此钩子;
    像User组件内通过setState修改userChangeTimes也会触发此钩子;
    6、React优化

    本小节介绍几项基本的React优化策略,有些是基于在React中使用Mobx时特有的策略,有些是会用React通用的策略。

    使用大量的小组件
    @observer 组件会追踪它们使用的所有值,并且当它们中的任何一个改变时重新渲染。 所以你的组件越小,它们需要重新渲染产生的变化则越小;这意味着用户界面的更多部分具备彼此独立渲染的可能性。

    在专用组件中渲染列表(避免多个组件受影响,一起重新渲染)
    不要使用数组的索引作为 key(虚拟dom)
    不用使用数组索引或者任何将来可能会改变的值作为 key

    晚一点使用间接引用值
    使用 mobx-react 时,推荐尽可能晚的使用间接引用值。 这是因为当使用 observable 间接引用值时 MobX 会自动重新渲染组件。 如果间接引用值发生在组件树的层级越深,那么需要重新渲染的组件就越少。

    展开全文
  • Mobx

    2020-04-27 12:25:43
    Mobx 开发文档 安装: npm install mobx --save 核心思想 通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。任何源自应用状态的东西都应该...

    Mobx

    开发文档

    安装:

    npm install mobx --save

    核心思想

    通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。任何源自应用状态的东西都应该自动地获得。其中包括UI、数据序列化、服务器通讯,等等。
    在这里插入图片描述
    React 和 MobX 是一对强力组合。React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而MobX提供机制来存储和更新应用状态供 React 使用。

    对于应用开发中的常见问题,React 和 MobX都提供了最优和独特的解决方案。React 提供了优化UI渲染的机制, 这种机制就是通过使用虚拟DOM来减少昂贵的DOM变化的数量。MobX 提供了优化应用状态与 React 组件同步的机制,这种机制就是使用响应式虚拟依赖状态图表,它只有在真正需要的时候才更新并且永远保持是最新的。

    基本使用

    定义状态并使其可观察

    import {observable} from 'mobx';
    
    var appState = observable({
        timer: 0
    });
    

    创建视图以响应状态的变化

    import {observer} from 'mobx-react';
    
    @observer
    class TimerView extends React.Component {
        render() {
            return (<button onClick={this.onReset.bind(this)}>
                    Seconds passed: {this.props.appState.timer}
                </button>);
        }
    
        onReset () {
            this.props.appState.resetTimer();
        }
    };
    
    ReactDOM.render(<TimerView appState={appState} />, document.body);
    

    更改状态

    appState.resetTimer = action(function reset() {
        appState.timer = 0;
    });
    
    setInterval(action(function tick() {
        appState.timer += 1;
    }), 1000);
    
    展开全文
  • MobX

    2019-06-27 18:12:25
    mobx MobX是一个经过实战测试的库,它通过透明地应用功能反应式编程(TFRP)使状态管理变得简单和可扩展,是一个类组件 通过@修饰一个组件。 步骤 1.下包 2.需要导出 npm run eject 3.再package.json里配置babel,...

    mobx

    MobX是一个经过实战测试的库,它通过透明地应用功能反应式编程(TFRP)使状态管理变得简单和可扩展,是一个类组件 通过@修饰一个组件。

    步骤

    1.下包
    2.需要导出 npm run eject
    3.再package.json里配置babel,支持装饰器语法
    4.和react和语法差不多 需要provider进行
    @abservable 模块的数据
    @action 模块的行为
    @connect

    注意事项

    1. 需要下载三个包
      “babel-plugin-transform-decorators-legacy”: “^1.3.5”, 支持装饰器
      “mobx”: “^5.7.0”, 支持mobx
      “mobx-react”: “^5.4.2” 支持合react集成
    2. 导出webapck配置
      npm run eject
    3. 再package.json里配置babel,支持装饰器语法
      “babel”: {
      “presets”: [
      “react-app”
      ],
      “plugins”: [
      ["@babel/plugin-proposal-decorators", { “legacy”: true }]
      ]
      }

    代码展示

    新建一个仓库 store /modules->count.js
    count.js
    import {observable, action} from 'mobx'
    
    export default class Count{
        // 模块的数据
        @observable count = 100;
    
        // 模块的行为
        @action changeCount(type){
            type==='+'?this.count++: this.count--
        }
    }
    
    store里的index.js
    // 加载子模块count
    import Count from './modules/count';
    
    // 引入日志
    import {autorun} from 'mobx';
    
    let count = new Count();
    
    autorun(()=>{
        console.log('count change...', count.count);
    })
    
    export default {
        count
    }
    
    App.js
    import React from 'react';
    import './App.css';
    import {inject, observer} from 'mobx-react'
    
    @inject('count')
    @observer
    class App extends React.Component{
      
      render(){
        console.log('this.props...', this.props);
        return <div>
          <button onClick={()=>this.props.count.changeCount('+')}>+</button>
          <p>{this.props.count.count}</p>
          <button onClick={()=>this.props.count.changeCount('-')}>-</button>
        </div>
      }
    }
    
    export default App;
     
    modx里的index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    
    // 引入Provider注入mobx数据
    import {Provider} from 'mobx-react'
    // 引入mobx的store
    import store from './store'
    
    ReactDOM.render(<Provider {...store}><App /></Provider>, document.getElementById('root'));
    

    仅供参考!

    展开全文
  • MobX Simple, scalable state management. MobX is made possible by the generosity of the sponsors below, and many other individual backers. Sponsoring directly impacts the longevity of this ...
  • Mobx坚持 $ npm install mobx-persist --save 用法 import { observable } from 'mobx' import { create , persist } from 'mobx-persist' class SomeItem { @ persist @ observable name = 'some' @ persist @ ...
  • Mobx原理: 手写mobx

    2020-09-19 22:59:43
    手写状态管理库: Mobx mobx是一个状态管理库,类似于redux或者vuex完成状态观察相应相关的处理。mobx使得状态管理更加的简单和透明。由于这篇文章时mobx原理解析,所以关于mobx的使用就不在这里记录。 注: 在开始...

    手写状态管理库: Mobx

    mobx是一个状态管理库,类似于redux或者vuex完成状态观察相应相关的处理。mobx使得状态管理更加的简单和透明。由于这篇文章时mobx原理解析,所以关于mobx的使用就不在这里记录。

    注: 在开始之前,需要先搭建起支持Es7语法的装饰器开发的webpack等开发环境. 这里主要实现Mobx的observable, autorun两个方法.

    • Mbox的observable的方法主要实现的是对状态的Proxy深度代理,使用Proxy对象代理状态对象的属性,同时在autorun方法使用的时候,会在这个代理过程中执行状态的依赖收集的相关操作.
    • 首先这里给一个简单的测试例子:
    // 测试 Mobx 库用例
    import { observable, autorun } from "./mobx" 
    // observable方法用于完成对状态对象的Proxy的代理,将状态变为可观察对象
    const o = observable({ name: "chensir" });
    // autorun方法类似于vue中的watcher,其中传递进去一个handler Function,这个回调函数会在初始化的时候被执行一次,之后每次内部相关的observable中的依赖发生变动时被再次调用
    autorun(() => {
    	console.log(o.name)
    })
    // 直接设置 o.name 值
    o.name = "chenSirLaiLe"
    
    // 测试用例会打印: chensir \n chenSirLaiLe
    
    • 实现observable方法:
    // 这里后面的两个参数: key 和 descriptor主要用于之后的装饰器实现
    export defualt function observable(target, key, descriptor){
    	// 这里支持装饰器模式的observable写法:
    	if(typeof key === "string"){
    		// 如果是作为装饰器装饰属性进行监听,先将装饰的对象进行深度代理
    		let v = descriptor.initializer();
    		v = createObservable(v);
    		// 这里执行依赖搜集: 使用的Reaction类会在之后实现
    		let reaction = new Reaction();
    		// 返回描述器
    		return {
    			enumerable: true,
    			configurable: true,
    			get(){
    				reaction.collect();  // 再获取target属性时进行autorun中的handler的依赖搜集
    				return v;
    			},
    			set(value){
    				v = value;
    				reaction.run();  // 在每次更新target中的属性时执行autorun中的依赖
    			}
    		}
    	}
    	// 如果不是装饰器写法,则创建Proxy代理
    	return createObservable(target);
    }
    
    // 创建代理对象
    function createObservable(val){
    	// 用于生成代理对象的控制器:
    	const handler = () => {
    		// 实例化Reaction在autorun获取属性的时候进行依赖搜集
    		let reaction = new Reaction();
    		return {
    			set(target, key, value){
    				// 对于数组的值设置处理: 当对数组进行观察监听时,由于对数组的操作会有两步执行:
    				// 更新数组元素值
    				// 更改数组的length属性,所以需要将更改length属性的操作给拦截,避免一次操作数组,多次触发handler
    				if(key === "length"){
    					return true;
    				}
    				// 执行搜集绑定, 此时修改值需要先执行,这样在autorun中的handler中才能拿到最新的值
    				let r = Reflect.set(target, key, value)
    				reaction.run();
    				return r;
    			},
    			get(target, key){
    				// 在获取属性值的时候进行依赖搜集
    				reaction.collect()
    				return Reflect.get(target, key);
    			}
    		}
    	}
    	// 进行深层Proxy代理返回: 针对如: {name: "chensir", age: {num: 21}}这样的对象
    	return deepProxy(val, handler)
    }
    
    // 深度设置Proxy对象代理
    function deepProxy(val, handler){
    	if(typeof val !== "object"){
    		return val;
    	}
    	// 深度递归进行Proxy代理,此时的递归树相当于是后序遍历进行代理
    	for(let key in val){
    		val[key] = deepProxy(val[key], handler);
    	}
    	return new Proxy(val, handler);
    }
    
    • 实现Reaction类进行状态搜集,作为abservable和autorun之间的桥梁:
    // 定义两个全局变量,这里是简单实现,所以和实际的源码实现有一定的区别
    let nowFn = null;  // 这个表示当前的autorun中的handler方法
    let counter = 0;  // 这里使用counter记录一个计数器值作为每个observable属性的id值进行和nowFn进行绑定
    
    class Reaction {
    	constructor(){
    		// 标识每一个proxy对象
    		this.id = ++counter;  // 这里采用一个比较low的方法简易实现的,在每次对observable属性进行Proxy的时候,对Proxy进行标记
    		this.store = {};  // 存储当前可观察对象对应的nowFn, 写入的形式如: {id: [nowFn]}
    	}
    	collect(){
    		// 进行依赖搜集,只当当前有autorun绑定了相关属性观察后才会进行绑定
    		if(nowFn){   // 通过这个判断主要是因为只有在调用autorun绑定的时候才会设置这里的nowFn
    			this.store[this.id] = this.store[this.id] || [];
    			this.store[this.id].push(nowFn);	
    		}
    	}
    	run(){
    		// 运行依赖函数
    		if(this.store[this.id]){
    			this.store[this.id].forEach(fn => {
    				fn()
    			})
    		}
    	}
    	// 定义两个静态方法,用于在调用autorun方法时候对nowFn进行设置和消除
    	static start(handler){
    		nowFn = handler;
    	}
    	// 在注册绑定这个就要清空当前的nowFn,用于之后进行进行搜集绑定
    	static end(){
    		nowFn = null;
    	}
    }
    
    • 实现autorun方法,进行简单的依赖搜集
    export default function autorun(handler){
    	if(typeof handler !== "function"){
    		throw new TypeError(`autorun function expect a function but get a ${typeof handler}`)
    	}
    	// 开始搜集依赖,设置Reaction中的nowFn
    	Reaction.start(handler)
    	// 执行一次handler,在handler中有对于相应属性的getter获取,此时就可以设置改属性的Proxy的Reaction状态依赖
    	handler()
    	// 清除nowFn
    	Reaction.end()
    }
    

    • 这里简单实现了Mobx的核心,当然和源码还是有一定的区别,所以不要将此处的代码和源码进行比对,我们只是希望去理解Mobx的状态观察搜集的原理而已。除此之外,Mobx还有一些其他的方法大家可以去了解了解。除此之外,Mobx在React框架中使用时,需要使用一个Mobx-react的库,这个库的实现原理也比骄简单这里提一下思路原理:
    • Mobx-React这个库在React Component组件中的state发生变动时手动调起组件的render方法和forceUpdate()对外部mobx更新后的状态在Component中进行强制刷新。
    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,080
精华内容 1,632
关键字:

mobx