更好用的基于mobx的数据流管理框架:Vane

vane

基于mobx & mobx-react的React store管理框架,提供简单快捷开发方式。

项目地址:Vane

特点

三个API搞定问题!简单易上手,开发效率高。

如何使用

vane提供了一键初始化的start方法,入口文件可以像下面这样开始:

import React from 'react';
import App from './App';

import {
    start,} from 'vane';

// model
import user from './models/User';
import todos from './models/Todos';

start({
    component: App,container: '#root',models: {
        user,todos
    }
});

所以,只需要把你的model(类似于Tarot的module)、React Container Component、Middleware(可选)、Relation传递进来,应用就能跑起来了。

介绍下几个概念:

  • model: 数据管理,区别于Tarot,其只有:name命名空间以及data、action两个核心部分,action部分可以同时存放类似于Reducers以及Effects两个部分的操作(作为优化,后续这里可以做拆分);

  • middleware:中间件,用于辅助异步处理。model重定义的一个action最终被执行的流程是这样的:首先其会被mobx的action函数包一层,以避免掉每次更改数据都会触发一次UI的重新渲染,然后其会被各个中间件依次执行,而每个中间件都有before/after/error三个操作,可以在不同的操作中对每一种操作做统一的处理;

  • relation:用于不同model之间的通信,基于监听订阅模式。

基于vane的开发范式的container Component也是UI Component,UI Component像下面这样:

import React,{Component,PropTypes} from 'react';

// load middlewares
import './middlewares';

// components
import UserLogin from './components/UserLogin';
import UserDetail from './components/UserDetail';
import Todos from './components/Todos';

import {
    inject,observer,} from 'vane';

// 注意先observer,后inject
@inject('user')
@observer
export default class App extends Component {
    render() {
        // const user = this.props.user.toJSON();
        console.log(this.props.user.toJSON());
        const {user} = this.props;

        console.log('user.isLogin:',user.isLogin);

        if (user.isLogin !== true) {
            return <UserLogin />;
        }

        return (
            <div>
                <UserDetail />
                <Todos />
            </div>
        );
    }
}

这里的oberser来自于mobx的observer,inject则来自于mobx-react。如果想给一个Component同时注入多个model,则可以像下面这样:

// start
import React from 'react';
import App from './App';

import {
    start,todos
    }
});
import {
    inject,} from 'vane';

@inject(
    stores => ({
        user: stores.user,todos: stores.todos,})
)
@oberser
class MyComponent extends Component{
    constructor(props,context) {
        super(props,context);
    }

    render() {
        const {
            user,todos,} = this.props;

        return (
            <div>{user.name}</div>
        );
    }
}

mobx的observer API,用于将React Component变成observable的(动态收集依赖),在对model中的某些数据做了操作之后,如果被修改的数据刚好被该React组件使用到了,就会触发该组件的重新渲染,这也就是mobx能细粒度控制数据的原因所在。

mobx-react的inject API,用于指定将哪些model注入进React Component(this.props.modelName),也就指定了该组件基于哪些数据做Observeable。

model

代码类似于下面这样:

import TodoItem from './TodoItem';
import * as api from '../api';

export default {
    name: 'Todos',data: {
        list: [],},syncs: {
        add(text,userId) {
            // 类似于Vue,对数组的操作会触发UI的重新渲染
            this.list.push(new TodoItem({
                text,userId
            }));
        },effects: {
        async getByUserId(userId) {
            let todos = await api.getTodosByUserId(userId);
            todos = todos.map(todo => new TodoItem(todo));
            // 类似于Vue,对数组的操作会触发UI的重新渲染
            this.list = this.list.concat(todos);
        },}
};

model由以下几个部分组成:

  • 1、name: 当前model的命名空间;

  • 2、constants: 不可变常量;

  • 3、data: 可操作数据部分;

  • 4、syncs: 同步操作数据部分;

  • 5、effects: 异步处理部分;

  • 6、init: 初始化model后的回调方法

  • 7、autorun: 每次对数据进行操作后都会自动执行的方法

触发action

model内部触发

model内部定义的Data数据,会被赋值到model实例上,所以任何在Data中定义的数据都可以通过this.xxx的方式来引用,如下:

import fetch from 'whatwg-fetch';

const pageSize = 20;

export default {
    name: 'Applications',data: {
        dataSource: [

        ],// 列表显示的数据

        detailPageVisible: false,campaignDetail: {},syncs: {
        validate(value) {
            value = value ||'';

            // xxxx

            return {
                code: 200
            };
        },effects:{
        async getList(payload = {}) {
            const {
                currentPage = 1,} = payload;

            const url = `/applications/list/${currentPage}?pageSize=${pageSize}`;

            let res = await fetch(url);

            res = res.body;

            const validateRes = this.validate(res);

            if(validateRes.code == 200) {
              this.dataSource = res.data; // 这样就会触发对应Component的重新渲染
              this.currentPage = res.currentPage;
              this.totalItem = res.totalItem;
              this.totalPage = res.totalPage;
            }

            return res;
        },}
};

可以看到,更改数据则是直接给model实例赋值即可,简单直接高效,而且多次赋值只会触发一次的重新渲染。你能想象如果一个页面一个list列表,用户对列表中某一个进行操作后,需要修改这一项的数据及显示,只需要执行类似于:

this.props.home.list[2].name = 'New Name';

代码就能完成name的数据处理及页面展示更改吗?想想就激动是不是。

组件内触发

如下,简单直接:

import { inject,observer } from 'vane';

@inject('applications')
@observer
class Applications extends Component {
    constructor(props,context);
    }

    clickHandler() {
      this.props.applications.getList(); // 直接执行
    }

    render() {
      return (
        <div onClick={::this.clickHandler}></div>
      );
    }
}

开发组件

有时候,我们并不想执行页面渲染,而是用Vane来开发一个组件,这时,还是可以使用start API,只要不传如container值,就会返回一个React Component。

import React from 'react';
import { render } from 'react-dom';
import App from './App';

// load middlewares
import middlewares from './middlewares';

import {
    start,} from 'vane';

// model
import user from './models/User';
import todos from './models/Todos';

// relation
import relation from './relations';

// 验证start返回一个组件
const MyComponent = start({
    component: App,todos
    },middlewares,relation
});

render(<MyComponent data={{a: 1}} />,document.querySelector('#root'));

特点

  • 简单易上手,开发效率高;

  • MVVM:Vane实现了基于React的MVVM开发范式,简单直接,开发效率高;

  • 更改store数据:直接赋值;

  • 触发action:直接执行store的action;

  • 性能优化:自动做掉。

具体例子参考example.

为什么基于mobx的开发范式更简单高效?

Mobx的实现思想和Vue几乎一样,所以其优点跟Vue也差不多:通过监听数据(对象、数组)的属性变化,可以通过直接在数据上更改就能触发UI的渲染,从而做到MVVM、响应式、上手成本低、开发效率高,在数据管理上需要再详细阐述下其区别。

Redux是建议全局唯一Store的,多个Reducers也会在传递给react-redux之前被合并成一个root reducer,任何数据的更改(通过Reducer)都会通过这一个store来触发整个UI树的重新渲染,如果不做任何的性能优化(pureRender等),就算VD(Virtual Dom)有了再高的效率提升,当页面数据量、DOM数量大了,性能消耗也是非常大的。另外一点,Redux实现的对数据的管理是pull方式的,就是说其只能等待应用派发某个行为(Action),然后重新触发UI的渲染,而做不到对行为的可预期;Mobx则不一样,他是基于监听数据的属性变化来实现的,而且是多store的,对于任何的数据变更都是第一时间知道的,所以其实现方式是基于push的监听订阅模式而实现,这样,他就可以做到对数据的可预测以及细粒度的控制,甚至可以通过修改React组件生命周期的方式来减少性能的消耗,而无需使用者对这些细节关心。当然这一切肯定是有了mobx对组件做observe操作才能实现的,所以也就有了observer用的越多,应用性能越高的说法。

感谢

Vane的部分实现参考自MVVM框架:mobx-roof

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom