更好用的數據流管理框架:Vanex發佈了~

vanex

基於mobx & mobx-react的React store管理框架,提供簡單快捷的開發範式。使用模式相似dva,但用起來比dva更簡單,開發效率更高! react

github地址: https://github.com/alibaba/vanex
example地址: https://github.com/alibaba/va...git

特色

三個API搞定問題!簡單易上手,開發效率高。github

如何使用

vanex提供了一鍵初始化的start方法,入口文件能夠像下面這樣開始:redux

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

import {
    start,
} from 'vanex';

// 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傳遞進來,應用就能跑起來了。api

介紹下幾個概念:數組

  • model: 數據管理,區別於tarot,其只有:name命名空間以及data、action兩個核心部分,action部分能夠同時存放相似於Reducers以及Effects兩個部分的操做(做爲優化,後續這裏能夠作拆分);
  • middleware:中間件,用於輔助異步處理。model重定義的一個action最終被執行的流程是這樣的:首先其會被mobx的action函數包一層,以免掉每次更改數據都會觸發一次UI的從新渲染,而後其會被各個中間件依次執行,而每一箇中間件都有before/after/error三個操做,能夠在不一樣的操做中對每一種操做作統一的處理;
  • relation:用於不一樣model之間的通訊,基於監聽訂閱模式。

基於vanex的開發範式的container Component也是UI Component,UI Component像下面這樣:性能優化

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

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

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

// 注意先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,則能夠像下面這樣:app

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

import {
    start,
} from 'vanex';

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

start({
    component: App,
    container: '#root',
    models: {
        user,
        todos
    }
});
import {
    inject,
    observer,
} from 'vanex';

@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。dom

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由如下幾個部分組成:

  • 一、name: 當前model的命名空間;
  • 二、constants: 不可變常量;
  • 三、data: 可操做數據部分;
  • 四、syncs: 同步操做數據部分;
  • 五、effects: 異步處理部分;
  • 六、init: 初始化model後的回調方法;
  • 七、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的數據處理及頁面展現更改嗎?想一想就激動是否是。

有的同窗會有:syncs和effects裏面屢次對model直接賦值會觸發UI的屢次渲染的擔憂,其實不會的,咱們隊syncs以及effects裏面的每個方法都用會使用mobx的action作了一層包裝,從而來避免這個問題。

另外,咱們也提供this.set()的輔助方法來方便的爲model改值,因此你還能夠這樣作:

this.set({
  dataSource: res.data,
  currentPage: res.currentPage,
  totalItem: res.totalItem,
  totalPage: res.totalPage,
});

這裏會使用mobx的runInAction來統一執行,從而保證UI渲染只執行一次。

組件內觸發

以下,簡單直接:

import { inject, observer } from 'vanex';

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

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

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

Vanex插件機制

Vanex支持插件機制,使用的方式以下:

import { start, use } from 'vanex';

import effectPlugin from './effect-plugin';

use(effectPlugin);

// start代碼

目前已經提供的插件列表以下:

onStateChange

用於監聽數據發生改變的時候的觸發回調。格式以下:

export default {
    onStateChange: [event => {
        console.log(event);
    }]
};

onEffect

用於處理異步執行執行前(before)、後(after)、錯誤(error)以及過濾哪些effects執行該回調,它在執行的時候實際上是以中間件的形式來執行的。若是有相似於每次請求都自帶csrfToken的需求,則能夠在before鉤子函數中組裝。

具體使用以下:

// Before exec action
function preLogger({
    type,
    payload
}) {
    console.log(`[${type}] params: `, payload);

    payload。csrfToken = 'xxx'; // 這裏的更改會對請求參數生效

    return payload;
}

// Action exec fail
function errorLogger({
    type,
    payload
}) {
    console.log(`[${type}] error: `, payload.message);
    return payload;
}

// After exec action
function afterLogger({
    type,
    payload
}) {
    console.log(`[${type}] result: `, payload);
    return payload;
}

export default {
    filter({
            type
        }) {
        return /^User/.test(type); // 只針對Model名字是User的進行下面鉤子函數的執行
    },
    before: preLogger,
    after: afterLogger,
    error: errorLogger,
};

onAction

用於在執行syncs Action以後觸發。格式以下:

export default {
    onAction: [(
        actionName,
        actionArgs,
        result) => {
            console.log(`當前執行Action的名字:${actionName}`);
            console.log(`當前執行Action的參數:${actionArgs}`);
            console.log(`當前執行Action的結果:${result}`);
        }]
};

getActionState

這個並非Vanex插件,可是用於解決在組件中獲取當前model中某個effect是否正在發送請求的問題,而這個狀態能夠用於方便的控制Loading組件是否可見。由於這種需求很是廣泛,因此Vanex直接內置到內部實現中。使用示例以下:

const {
    user
} = this.props;

const {
    loading: loginLoading,
    error: loginError
} = user.getActionState('user/login');

用於開發組件

有時候,咱們並不想執行頁面渲染,而是用Vanex來開發一個組件,這時,仍是可使用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,
    use,
} from 'vanex';

use({
    onEffect: middlewares
});

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

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

// 驗證start返回一個組件
const MyComponent = start({
    component: App,
    models: {
        user,
        todos
    },
    relation
});

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

特色

  • 簡單易上手,開發效率高;
  • MVVM:Vanex實現了基於React的MVVM開發範式,簡單直接,開發效率高;
  • 更改store數據:直接賦值;
  • 觸發action:直接執行store的action;
  • 性能優化:自動作掉。

爲何基於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用的越多,應用性能越高的說法。

感謝

Vanex的部分實現參考自MVVM框架:mobx-roof

落地

相關文章
相關標籤/搜索