Nearly
一个简约, 壮大的数据流框架; Github
装置
npm install --save nearly-react
特征
上图为 flux 架构图, Nearly 参考自 flux, 在其基础上做了以下简化和革新:
功能上:
集成
Promise
, 我们不再须要多写一个componentDidMount
要领去异步猎取数据, 更多状况下, 我们将运用stateless component
让代码越发简约;Store
的运用越发天真,Store
的单实例和多实例运用能很奇妙地完成跨组件通讯和通用组件掌握逻辑的复用;
比拟 flux:
API 越发简约, 在营业中平常只会用到
connect
和dispatch
要领;对状况举行集合治理, 写法与原始的
React
类似, 进修和迁徙成本低;更轻量, min 后只需 6K;
运用示例
import React from 'react';
import { render } from 'react-dom';
import {connect, dispatch, registerStore} from 'nearly-react';
registerStore('counter', {
// 必需完成 init 要领, 它将被隐式挪用, 作用是初始化 state
init() {
return {
count: 0
};
},
add(getState, step) {
return {
count: getState().count + step
};
}
};
let incr = () => dispatch('counter::add', 1);
let decr = () => dispatch('counter::add', -1);
function Counter(props) {
return (
<div>
<button onClick={incr}> - </button>
<span>{props.count}</span>
<button onClick={decr}> + </button>
</div>
)
}
let HocCounter = connect('counter', Counter);
render(
<HocCounter />,
document.getElementById('root')
)
API
registerStore(storeName, dispatcherSet)
该要领将注册一个 Store
, 须要注重的是该要领必需先 connect
实行, 例:
registerStore('customStore', {
// 必需完成 init 要领
init() {
return {sum: 0};
},
add(getState, num) {
return {sum: getState().sum + num};
}
});
Dispatcher functions(getState, …args)
registerStore
接收的第二个参数里的要领即 Dispatcher functions
;Dispatcher function
的第一个参数为 getState
要领, 该要领返回的永远是当前最新的 state
, 其他参数为 dispatch
要领所传的参数;
关于 Dispatcher function
的返回值:
为一般对象时, 返回值直接 merge 进旧 state;
为
Promise
时, 取Promise.prototype.then
要领里的参数 merge 进旧 state;为
null
时, 不 merge, 不触发 render;
例:
registerStore('counter', {
// 必需完成 init 要领, init 中也能够运用 Promise
init() {
return fetch('./test.json').then(res => res.json());
},
add(getState, step) {
return {
count: getState().count + step
};
},
// 异步增添
addAsync(getState, step) {
return new Promise(resolve => {
setTimeout(() => {
// getState 要领返回的永远是最新的 state
let count = getState().count + step;
resolve({count})
}, 1000);
});
},
// 不触发衬着
nothing(getState, step) {
return null;
}
};
dispatch(action, …args)
默许设置的 action
花样为 ${storeName}::${function}
,
dispatch 会依据 action
映照到响应的 Dispatcher function
, 并将 args 作为参数传入 Dispatcher function
, 将其返回的结果提交给 Store
, 由 Store
触发组件更新;
connect(storeName, Component [, PlaceHolder, isPure])
该要领会依据 storeName
取得 Store
, 再将 Store
, Component
和 PlaceHolder
组合, 返回一个高阶组件;
个中, PlaceHolder
为默许展现组件 (可选), 当且仅当 init
返回 Promise
时有用, 在 Component
被插进去 dom 之前, 组合后的高阶组件会先展现 PlaceHolder
组件, 可用于完成 loading 之类的结果;
但组件过大时, 能够经由过程设置 isPure
为 true 来进步机能, 当设置 isPure
为 true 时, 只需 dispatch
要领能触发组件的 render
, 我置信这比经由过程在 shouldComponentUpdate
里写 shallowEqual
要有用很多;
也能够经由过程下面的 configure
设置默许的 isPure
为 true;
进阶运用
dispatcher(action, …args)
即 dispatch
的高阶函数; 例:
dispatch('counter::add', 1);
等同于: dispatcher('counter::add')(1);
dispatch('test::testAdd', 1, 2, 3, 4);
等同于: dispatcher('test::testAdd', 1, 2)(3, 4);
configure(option)
运用 nearly
举行开辟, 我们须要斟酌 storeName
反复的状况, 我引荐经由过程将 storeName
映照文件途径的体式格局来防止;
nearly
供应了两个可供设置的要领: beforeConnect
和 beforeDispatch
;
beforeConnect
会在connect
要领被挪用之前挪用, 接收的参数为传入connect
要领的storeName
; 我们能够用它去加载对应的 JS 文件, 并注册Store
;beforeDispatch
会在dispatch
要领被挪用之前挪用, 接收的参数为传入dispatch
要领的action
;
默许设置以下:
import {registerStore, getStore} from './store';
let config = {
// 默许的 isPure
defaultPure: false,
// 默许不开启自动注册 Store
beforeConnect(storeName) {
// let store = getStore(storeName);
// if (!store) {
// let realName = storeName.split('#')[0];
// registerStore(storeName, require(`./actions/${realName}.js`));
// }
},
beforeDispatch(action) {
let [storeName, dispatcherName] = action.split('::');
let store = getStore(storeName);
if (!store) {
throw Error(`store '${storeName}' does not exist`);
}
let dispatcher = store.dispatchers[dispatcherName];
if (!dispatcher) {
throw Error(`the module does not export function ${dispatcherName}`);
}
return {store, dispatcher};
}
}
运用示例:
import {configure, getStore, registerStore} from 'nearly-react';
configure({
beforeConnect(storeName) {
// 设置 beforeConnect 要领, 自动注册 Store
// 当 store 不存在时
// 自动去 actions 目录下加载 JS 模块, 并注册 Store
let store = getStore(storeName);
if (!store) {
let realName = storeName.split('#')[0];
registerStore(storeName, require(`./actions/${realName}.js`));
}
}
});
统一 Store 单实例运用
在营业中我们常常须要跨组件通讯, 或许组件间同享数据;
运用 Nearly 我们能很轻易地将两个差别的组件绑定雷同的 Store
, 只需传入 connect
的 storeName
是雷同的即可;
例: 简朴的输入同步显现
registerStore('vm', {
// 必需完成 init 要领, 它将被隐式挪用, 作用是初始化 state
init() {
return {
value: ''
};
},
change(getState, value) {
return {
return { value };
};
}
};
// /components/Input.js
let change = e => dispatch('vm::change', e.target.value);
function Input(props) {
return <input value={props.value} onChange={change} />
}
export default connect(Input, 'vm');
// /components/Text.js
function Text(props) {
return <p>{props.value}</p>
}
export default connect(Text, 'vm');
详见示例: One-store
统一 Store 多实例运用
我们开辟通用组件时会须要给统一组件绑定统一 store
的差别实例以复用; 能够经由过程给 storeName
加上 #id
来辨别差别 Store
;
// Dialog.js
export default function Dialog (props){
return <div>{props.content}</div>
}
let DialogA = connect(Dialog, 'dialog#a');
let DialogB = connect(Dialog, 'dialog#b');
// 封闭弹窗 A
dispatch('dialog#a::close');
// 封闭弹窗 B
dispatch('dialog#b::close');
注重, 当在组件内部运用 dispatch
时, 能够经由过程 props._storeName
来肯定 storeName
;
详见示例: Dialog
示例
Tips
nearly-config.js
必需在营业逻辑之前加载;虽然有
registerStore
API, 不过作者照样引荐运用connect
来隐式注册Store
, 由于connect
经由过程storeName
映照文件的体式格局来注册Store
, 在确保唯一性的同时更轻易保护和 debug;在 Nearly 中对
Promise
的推断是不正确的 (只需有then
要领均认为是Promise
实例) , 一方面是由于 Nearly 中只运用了then
要领, 另一方面是为了兼容jQuery.Deferred
等类库;迎接提 issue 或是 pr;