# natur 使用手册
# 基本介绍
- 这是一个简洁、高效的react状态管理器
- 良好的typescript体验
- 单元测试覆盖率99%,放心使用
- 包体积,minizip 5k(uglify+gzip压缩后5k)
- 如果你的环境是react@17或者更低版本,你可以使用natur@2.1.x
# 起步
- 打开你的react项目
- 安装
natur
yarn add natur
# npm install natur -S
# 设计
# 模块流程
# 模块管理
natur本身是一个模块管理器,外加发布订阅
# 简单的示例
# 声明模块
const count = {
// 存放数据
state: {
number: 0,
},
// state的映射
maps: {
isEven: ['number', number => number % 2 === 0],
},
// actions用来修改state
actions: {
inc: number => ({number: number + 1}),
dec: number => ({number: number - 1}),
}
}
# 创建store和inject
import { createStore, createUseInject } from 'natur';
const store = createStore({count}, {});
const useInject = createUseInject(() => store);
const useFlatInject = createUseInject(() => store, {flat: true});
# 在React中使用
// 创建一个count模块的注入器
// 声明props类型
const App = () => {
const [count] = useInject('count');
const [flatCount] = useFlatInject('count');
// flatCount中的数据是扁平的,即state、action、maps中的数据被放到一个对象中(state数据会被maps覆盖,请注意命名)
return (
<>
<button onClick={() => count.actions.dec(count.state.number)}>-</button>
<span>{count.state.number}</span>
<button onClick={() => count.actions.inc(count.state.number)}>+</button>
<span>{count.maps.isEven}</span>
</>
)
};
ReactDOM.render(<App />, document.querySelector('#app'));
# module详解
一个模块由state, maps, actions构成
# state — 存储数据
- 必填:
true
- 类型:
any
- state用来存储数据
# maps — 计算属性
必填:
false
类型:
{[map: string]: Array<string|Function> | Function;}
maps是state数据的映射,它的成员必须是一个数组或者函数,我们暂且称其为map
如果map是数组,前面的元素都是在声明此map对state的依赖项。最后一个函数可以获取前面声明的依赖,你可以在里面实现你需要的计算的逻辑。在组件中,你可以获取数组最后一个函数运行的结果。
如果map是函数,那么它只能接受state作为入参,或者没有参数,如果是state作为参数,那么当state更新时,此map一定会重新执行,没有缓存。如果map没有参数,那么此map只会执行一次
maps的结果是有缓存的,如果你声明的依赖项的值没有变化,那么最后一个函数便不会重新执行
什么时候需要手动声明依赖?如果你的map逻辑较为复杂,或者你的map返回值不是基本类型的值,需要给到组件渲染,那么你可以考虑手动声明依赖,保证性能。一般直接使用函数的方式即可。
const demo = {
state: {
number: 1,
value: 2
},
maps: {
// 数组前面的元素,都是在声明此map对state的依赖项,最后一个函数可以获取前面声明的依赖,你可以在里面实现你想要的东西
sum1: ['number', 'value', (number, value) => number + value],
// 你也可以通过函数的方式声明依赖项,这对于复杂类型的state很有用
sum2: [state => state.number, s => s.value, (number, value) => number + value],
// 也可以是个函数,直接依赖整个state,缺点是只要state更新就会重新执行函数,没有缓存
sum3: ({number, value}) => number + value,
// 也可以是个函数,没有依赖,只执行一次
isTrue: () => true,
},
}
/**
* 在组件中你获得的数据为
* demo: {
* state: {
* number: 1,
* value: 2,
* }
* maps: {
* sum1: 3,
* sum2: 3,
* sum3: 3,
* isTrue: true
* }
* ...
* }
*/
# actions — 更新数据
- 必填:
true
- 类型:
{[action: string]: (...arg: any[]) => any;}
- actions的成员必须是函数,如果不设置中间件,那么它返回的任何数据都会作为新的state,并通知使用此模块的react组件更新,这是在natur内部完成的。
- actions必须遵照immutable规范!
const demo = {
state: {
number: 1,
},
// actions用来修改state。它返回的数据会作为新的state(这部分由natur内部完成)
actions: {
inc: number => ({number: number + 1}),
dec: number => ({number: number - 1}),
}
}
/**
* 在组件中你获得的数据为
* demo: {
* state: {
* number: 1,
* }
* actions: {
* inc: (number) => 新的state,
* dec: (number) => 新的state,
* }
* ...
* }
*/
# watch — 监听模块变动
- 必填:
false
- 类型:
{[moduleName: string]: (event: WatchEvent, api: WatchAPI) => void;} | (event: AllModuleEvent, api: WatchAPI) => void;
- watch可以监听某个模块的变动,也可以监听所有模块的变动
- 在watch里面可以获取当前模块的state,maps,也可以调用localDispatch来调用本模块的action
const demo = {
state: {
number: 1,
},
actions: {
inc: number => ({number: number + 1}),
dec: number => ({number: number - 1}),
},
watch: {
moduleA: (event: ModuleEvent, api: WatchAPI) => {
// 任何 moduleA 的变动都会触发这个函数,具体的变动信息在event参数获取
// 例如moduleA的初始化,更新state,销毁都可以监听到
// api参数包含demo模块的, getState, getMaps, localDispatch等API, 以及获取全局store的getStoreAPI.
// localDispatch只能调用本模块的action,例如:localDispatch('inc', 2);
}
}
}
const moduleA = {
state: {
number: 1,
},
actions: {
inc1: number => ({number: number + 1}),
dec1: number => ({number: number - 1}),
},
// watch也可以是一个函数用来监听所有模块的变动
watch: (event: AllModuleEvent, api: WatchAPI) => {
// 任何模块的变动都会触发这个函数,包括moduleA模块自己,具体的变动信息在event参数获取
// api参数包含moduleA模块的, getState, getMaps, localDispatch等API, 以及获取全局store的getStoreAPI.
// localDispatch是只能调用本模块的action,例如:localDispatch('inc1', 2);
}
}
# 应用场景
# 注入多个模块
// 导入你之前创建的inject函数,详情请参考上面的简单例子
import useInject from 'your-use-inject';
// 在你的组件中
const [module1] = useInject('module1');
const [module2] = useInject('module2');
// ...
# 同步更新数据
- 这里我们默认使用官方推荐的中间件配置, 详情请看中间件部分
const app = {
state: {
name: "tom",
},
actions: {
// 这里是同步更新state中的name数据
changeName: newName => ({ name: newName }),
}
};
# 异步更新数据
- 这里我们默认使用官方推荐的中间件配置, 详情请看中间件部分
const app = {
state: {
name: "tom",
},
actions: {
// 这里是异步更新state中的name数据
changeName: newName => Promise.resolve({ name: newName }),
}
};
# 异步多批次更新数据
- 这里我们默认使用官方推荐的中间件配置, 详情请看中间件部分
import { ThunkParams } from "natur/dist/middlewares";
const state = {
now: Date.now(),
}
const actions = {
// 这里是异步多批次更新state中的name数据
updateNow: () => ({setState}: ThunkParams<typeof state>) => {
// 每秒更新一次now的值
setInterval(() => setState({now: Date.now()}), 1000);
},
}
const app = {
state,
actions
};
# 在actions中获取最新的state,maps值
- 这里我们默认使用官方推荐的中间件配置, 详情请看中间件部分
import { ThunkParams } from "natur/dist/middlewares";
const state = {
name: 'tom',
}
const maps = {
nameIsTome: ['name', (name: string) => name === 'tom'],
}
const actions = {
updateName: () => ({getState, getMaps}: ThunkParams<typeof state, typeof maps>) => {
// 获取最新的state值
const currentState = getState();
// 获取最新的maps值
const currentMaps = getMaps();
},
}
const app = {
state,
maps,
actions
};
# 在actions中调用其他的action
- 这里我们默认使用官方推荐的中间件配置, 详情请看中间件部分
import { ThunkParams } from "natur/dist/middlewares";
const state = {
name: 'tom',
loading: true,
}
const actions = {
loading: (loading: boolean) => ({loading}),
fetchData: (newName: string) => async ({localDispatch}: ThunkParams) => {
// 调用loading方法
localDispatch('loading', true);
await new Promise(resolve => setTimeout(resolve, 3000));
localDispatch('loading', false);
return {name: newName};
},
}
const app = {
state,
actions
};
# 组件只监听部分数据的变更
// 导入你之前创建的inject函数,详情请参考上面的简单例子
import useInject from 'your-use-inject';
// 这里App组件只会监听app,state中name的变化,其他值的变化不会引起App组件的更新
const [app] = useInject('app', {
state: ['name'], // 也可以使用函数声明 state: [s => s.name]
});
// 这里App组件只会监听app,maps中deepDep的变化,其他值的变化不会引起App组件的更新
const [app] = useInject('app', {
maps: ['deepDep'],
});
// 这里App组件不论app模块发生什么变化,都不会更新
const [app] = useInject('app', {});
// 因为actions在创建后会保持不变,所以你不必监听它的变化
const App = () => {
const [app] = useInject('app');
// 获取注入的app模块
const {state, actions, maps} = app;
return (
<input
value={state.name} // app中的数据
onChange={e => actions.changeName(e.target.value)}
/>
)
};
# 懒加载模块配置
/*
module1.js
export {
state: {
count: 1,
}
actions: {
inc: state => ({count: state.count + 1}),
}
}
*/
const otherLazyModules = {
// module2: () => import('module2');
// ...
}
const module1 = () => import('module1'); // 懒加载模块
// 创建store实例
// 第二参数就是懒加载的模块;
const store = createStore(
{ app },
{ module1, ...otherLazyModules }
);
// 然后用法等同于第二步
# 初始化state
import { createStore } from 'natur';
const app = {
state: {
name: 'tom',
},
actions: {
changeName: newName => ({ name: newName }),
asyncChangeName: newName => Promise.resolve({ name: newName }),
},
};
/*
createStore第三个参数
{
[moduleName: ModuleName]: Require<State>,
}
*/
const store = createStore(
{ app },
{},
);
store.globalSetStates({
app: {name: 'jerry'} // 设置app 模块的state
})
export default store;
# 跨模块的交互的复杂业务场景
你可以使用模块中的watch功能,他可以监听任何模块的任何变动,并且你可以发起你想要的dispatch
import { ModuleEvent, AllModuleEvent, WatchAPI } from 'natur';
export const moduleA = {
state: {},
actions: {/* ... */},
watch: {
moduleB(event: ModuleEvent, api: WatchAPI) {
// 任何 moduleB 的变动都会触发这个函数,具体的变动信息在event参数获取
// api参数包含本模块的, getState, getMaps, localDispatch等API, 以及获取全局store的getStoreAPI.
// localDispatch是只能调用本模块的action,例如:localDispatch('actionNameA', ...actionAArgs);
}
}
}
export const moduleB = {
state: {},
actions: {/* ... */},
// watch也可以是一个函数用来监听所有模块的变动
watch: (event: AllModuleEvent, api: WatchAPI) => {
// 任何模块的变动都会触发这个函数,具体的变动信息在event参数获取
// api参数包含本模块的, getState, getMaps, localDispatch等API, 以及获取全局store的getStoreAPI.
// localDispatch是只能调用本模块的action,例如:localDispatch('actionNameA', ...actionAArgs);
}
}
# 在react之外使用natur
// 引入之前创建的store实例
import store from 'my-store-instance';
/*
获取注册的app模块, 等同于在react组件中获取的app模块
如果你想要获取懒加载的模块,
那么你必须确定,这个时候该模块已经加载好了
*/
const app = store.getModule('app');
/*
如果你确定,懒加载模块,还没有加载好
你可以监听懒加载模块,然后获取
*/
store.subscribe('lazyModuleName', () => {
const lazyModule = store.getModule('lazyModuleName');
});
/*
state: {
name: 'tom'
},
actions: {
changeName,
asyncChangeName,
},
maps: {
splitName: ['t', 'o', 'm'],
addName: lastName => state.name + lastName,
}
*/
/*
当你在这里使用action方法更新state时,
所有注入过app模块的组件都会更新,
并获取到最新的app模块中的数据,
建议不要滥用
*/
app.actions.changeName('jerry');
// 等同于
store.dispatch('app', 'changeName', 'jerry');
/**
*
* type: 模块变动的类型
* init: 模块初始化事件
* update: 模块state更新事件
* remove: 模块移除事件
*
* actionName: 模块更新state时的action名字
*/
type ModuleEvent = {
type: 'init' | 'update' | 'remove',
actionName?: string,
};
// 监听模块变动
const unsubscribe = store.subscribe('app', (me: ModuleEvent) => {
// 这里可以拿到最新的app数据
store.getModule('app');
});
// 取消监听
unsubscribe();
# 手动导入模块
// initStore.ts
import { createStore } from 'natur';
// 在实例化store的时候,没有导入懒加载模块
export default createStore({/*...modules*/}, {});
// ================================================
// lazyloadPage.ts 这是一个懒加载的页面
import store from 'initStore.ts'
const lazyLoadModule = {
state: {
name: 'tom',
},
actions: {
changeName: newName => ({ name: newName }),
},
maps: {
nameSplit: state => state.name.split(''),
addName: state => lastName => state.name + lastName,
},
};
/*
手动添加模块,在此模块被添加之前,其他地方无法使用此模块
要想其他地方也使用,则必须在store实例化的时候就导入
*/
store.setModule('lazyModuleName', lazyLoadModule);
const lazyLoadView = () => {
// 现在你可以获取手动添加的模块了
const {state, maps, actions} = store.getModule('lazyModuleName');
return (
<div>{state.name}</div>
)
}
# dispatch
import { createStore, inject, InjectStoreModule } from 'natur';
const count = {
state: { // 存放数据
number: 0,
},
maps: { // state的映射。比如,我需要知道state中的number是否是偶数
isEven: ['number', number => number % 2 === 0],
},
actions: { // 用来修改state。返回的数据会作为新的state(这部分由natur内部完成)
inc: number => ({number: number + 1}),
dec: number => ({number: number - 1}),
}
}
// 创建store这一步需要在渲染组件之前完成,因为在组件中,需要用到你创建的store
const store = createStore({count}, {});
const {actions, state} = store.getModule('count')
actions.inc(state.number);
// 等于
store.dispatch('count', 'inc', state.number);
# 不想写那么多typescript代码? (NaturBaseFactory)
不想写maps那么多类型?
import { NaturBaseFactory } from 'natur'; const state = { count: 1, }; const createMap = NaturBaseFactory.mapCreator(state); const maps = { isOdd: createMap( // 这里的s无需手动声明类型 s => s.count, // 这里的count无需手动声明类型,自动推导出前面的返回数据类型 count => count % 2 === 1 ) }
不想写actions类型?
一般插件都建议重写此类方法,以符合插件的类型提示,例如natur-immer的NaturFactory.actionsCreator
import { NaturBaseFactory } from 'natur'; const state = { count: 1, }; const createMap = NaturBaseFactory.mapCreator(state); const maps = { isOdd: createMap( s => s.count, count => count % 2 === 1 ) } // 第二个参数是可选的,没有maps可以不用传 const createActions = NaturBaseFactory.actionsCreator(state, maps); const actions = createActions({ // 这里的api类型会自动提示,不需要再手动声明 updateCount: (count: number) => api => { api.setState(count) } })
不想写watch
import { NaturBaseFactory } from 'natur'; /** * 第一个参数是你想 */ const createWatch = NaturBaseFactory.watchCreator(module1, state, maps); const watch = createWatch({ // event和api的类型都会自动推导出,无需手动声明 module1Name: (event, api) => { // xxx } })
# 拦截器
在模块调用action或者store.dispatch时会先经过interceptor,因此拦截器可以应用于,控制action是否执行,以及action的入参控制等场景
import {
createStore,
Interceptor
InterceptorActionRecord,
InterceptorNext,
InterceptorParams,
} from 'natur';
const app = {
state: {
name: 'tom',
},
actions: {
changeName: newName => ({ name: newName }),
asyncChangeName: newName => Promise.resolve({ name: newName }),
},
};
type InterceptorActionRecord = {
moduleName: String;
actionName: String;
actionArgs: any[];
actionFunc: (...arg: any) => any; // 原始的action方法
}
type InterceptorNext = (record: InterceptorActionRecord) => ReturnType<Action>;
// InterceptorParams类型于MiddlewareParams类型相同
type InterceptorParams = {
setState: MiddlewareNext,
getState: () => State,
getMaps: () => InjectMaps,
dispatch: (action, ...arg: any[]) => ReturnType<Action>,
};
const LogInterceptor: Interceptor<typeof store.type> = (interceptorParams) =>
(next: InterceptorNext) =>
(record: InterceptorActionRecord) => {
console.log(`${record.moduleName}: ${record.actionName}`, record.actionArgs);
return next(record); // 你应该return, 只有这样你在页面调用action的时候才会有返回值
};
const store = createStore(
{ app },
{},
{
interceptors: [LogInterceptor, /* ...moreInterceptor */]
}
);
export default store;
# 中间件
中间件的执行发生在action执行之后,更新state之前。可以接收action的返回值,一般可以应用于action返回值的加工,state更新的控制等行为
import {
createStore,
MiddleWare,
MiddlewareNext,
MiddlewareActionRecord
} from 'natur';
const app = {
state: {
name: 'tom',
},
actions: {
changeName: newName => ({ name: newName }),
asyncChangeName: newName => Promise.resolve({ name: newName }),
},
};
type MiddlewareActionRecord = {
moduleName: String,
actionName: String,
state: ReturnType<Action>,
}
type MiddlewareNext = (record: MiddlewareActionRecord) => ReturnType<Action>;
type middlewareParams = {
setState: MiddlewareNext,
getState: () => State,
getMaps: () => InjectMaps,
dispatch: (action, ...arg: any[]) => ReturnType<Action>,
};
const LogMiddleware: MiddleWare<typeof store.type> = (middlewareParams) =>
(next: MiddlewareNext) =>
(record: MiddlewareActionRecord) => {
console.log(`${record.moduleName}: ${record.actionName}`, record.state);
return next(record); // 你应该return, 只有这样你在页面调用action的时候才会有返回值
// return middlewareParams.setState(record); // 你应该return,只有这样你在页面调用action的时候才会有返回值
};
const store = createStore(
{ app },
{},
{
middlewares: [LogMiddleware, /* ...moreMiddleware */]
}
);
export default store;
# 内置中间件说明
# thunkMiddleware: thunk中间件可以使得action可以返回函数,拥有了获取最新的state,maps,以及setState,dispatch等增强功能
如果你喜欢mutable的写法,推荐使用natur-immer
import { thunkMiddleware, ThunkParams } from 'natur/dist/middlewares'
const actionExample = (myParams: any) => ({
getState,
setState,
getMaps,
dispatch
}: ThunkParams<typeof stateOfThisModule, typeof mapsOfThisModule>) => {
const currentState = getState(); // 最新的state
const currentMaps = getMaps(); // 最新的maps
// dispatch('otherActionNameOfThisModule', ...params)
// dispatch('otherModuleName/otherActionNameOfOtherModule', ...params);
setState(currentState); // 更新state
return currentState; // 更新state
}
# promiseMiddleware: action支持异步操作
// promiseMiddleware
const action1 = () => Promise.resolve(2333);
const action2 = async () => await new Promise(res => res(2333));
# fillObjectRestDataMiddleware: state增量更新/覆盖更新,state是对象时才有效
const state = {
a: 1,
b: 2
};
/**
* 调用此action,最后的state是{a: 11, b:2}此中间件要求
* state和action返回的数据必须都是普通对象
*/
const action = () => ({a: 11})
# shallowEqualMiddleware:浅层比较优化中间件,仅限于普通对象的state
const state = {
a: 1,
b: 2
};
const action = () => ({a: 1, b:2}) // 与旧的state相同,不做更新视图
# filterUndefinedMiddleware: 过滤返回undefined的action操作
const action = () => undefined; // 这种action的返回不会作为新的state
# devtool:开发调试工具
// redux.devtool.middleware.ts
import { Middleware } from 'natur';
import { createStore } from 'redux';
const root = (state: Object = {}, actions: any):Object => ({
...state,
...actions.state,
});
const createMiddleware = ():Middleware => {
if (process.env.NODE_ENV === 'development' && (window as any).__REDUX_DEVTOOLS_EXTENSION__) {
const devMiddleware = (window as any).__REDUX_DEVTOOLS_EXTENSION__();
const store = createStore(root, devMiddleware);
return ({getState}) => next => record => {
store.dispatch({
type: `${record.moduleName}/${record.actionName}`,
state: {
[record.moduleName]: record.state || getState(),
},
});
return next(record);
}
}
return () => next => record => next(record);
}
export default createMiddleware();
# 推荐的中间件配置
注意:中间件配置的先后顺序很重要
import {createStore} from 'natur';
import {
thunkMiddleware,
promiseMiddleware,
fillObjectRestDataMiddleware,
shallowEqualMiddleware,
filterUndefinedMiddleware,
} from 'natur/dist/middlewares';
import devTool from 'redux.devtool.middleware';
const store = createStore(
modules,
{},
{
middlewares: [
thunkMiddleware, // action支持返回函数,并获取最新数据
promiseMiddleware, // action支持异步操作
fillObjectRestDataMiddleware, // 增量更新/覆盖更新
shallowEqualMiddleware, // 新旧state浅层对比优化
filterUndefinedMiddleware, // 过滤无返回值的action
devTool, // 开发工具
],
}
);
# typescript支持
# 基础用法
import React from 'react';
import ReactDOM from 'react-dom';
// 导入你之前创建的inject函数,详情请参考上面的简单例子
import inject from 'your-inject';
import {ModuleType, Store} from 'natur';
const count = {
state: { // 存放数据
number: 0,
},
maps: { // state的映射。比如,我需要知道state中的number是否是偶数
isEven: ['number', number => number % 2 === 0],
},
actions: { // 用来修改state。返回的数据会作为新的state(这部分由natur内部完成)
inc: number => ({number: number + 1}),
dec: number => ({number: number - 1}),
}
}
// 生成count模块在组件中获得的类型
type InjectCountType = ModuleType<typeof count>;
const injector = inject('count');
type otherProps = {
className: string,
style: Object,
}
const App: React.FC<typeof injector.type & otherProps> = (props) => {
const {state, actions, maps} = props.count;
return (
<>
<button onClick={() => actions.inc(state)}>+</button>
<span>{state.count}</span>
<button onClick={() => actions.dec(state)}>-</button>
</>
)
}
const IApp = injector(App);
const app = (
<IApp className='1' style={{}} />
);
ReactDOM.render(
app,
document.querySelector('#app')
);
# 重新定义store类型
import {Store, createStore} from 'natur';
const count = {
/* ... */
}
const lazyModule1 = () => import(/* ... */);
const allSyncModules = {
count,
/* and others */
}
const allAsyncModules = {
lazyModule1,
/* and others */
}
const store = createStore(allSyncModules, allAsyncModules);
type StoreInsType = Store<typeof allSyncModules, typeof allAsyncModules>;
// StoreInsType的类型就是store的类型,你可以扩展你的类型
# 为什么选择NATUR
# 系统设计理念
natur
的初衷是简单自然的掌管项目中所有的业务逻辑,在这方面是不同于redux (opens new window)或者mobx (opens new window)这样的状态管理库。natur
可以轻松的让项目中所有的业务与UI
层松耦合,这可以让UI
保持足够的简单和纯粹,对于项目的维护性有着很大的好处。- 模块的
state
包含了业务数据的存储。maps
包含了state
衍生数据的逻辑,以及缓存的设计保证性能,值得一提的是,maps
使用了手动声明依赖的方式,这也与是react
一贯的设计风格相符合。action
包含了state
数据更新,以及其他业务逻辑(比如一个没有返回值的API的调用),natur
推荐action
设计的职责明确,一个action
只做一件事原则。如果能让每个action
的执行有对应state
变化,那么这能够让整个项目具有可观测和追踪性,并且可以更好的为模块间的通讯服务(可观测和追踪性可以通过拦截器和中间件来实现)。 - 在模块通讯这里,watch可以很好的监听模块动作,并解耦模块之间依赖
- 在
natur
模块的设计方面,则是推荐用户细分模块,明确模块的边界,以及粒度,以保证模块的可维护性。确保一个natur
模块中只处理自己的业务,而不需要关心其他模块,与别的模块没有耦合。 - 因为
natur
模块的创建和使用方式足够的简单,所以使得开发在设计方面能够尽可能简单自然的写出符合natur
设计理念的项目,当然最主要的还是开发人员需要明白natur
的设计理念并遵循它。
# 与redux对比
- 首先redux (opens new window)是全局状态管理器,这与
natur
的项目业务逻辑管理的设计目标是不同的。 - 其次redux (opens new window)的使用成本比较高,
natur
使用则是非常的简单 - 性能方面,在
natur
中存在缓存、模块懒加载,以及部分监听功能的支持,你不需要额外的库来保证你的项目性能。
# 与mobx的对比
- 兼容性方面,因为mobx (opens new window)使用了
proxy
或者defineProperty
等API
所以兼容性要稍微差点 - 在模块通讯方面,mobx的设计无法完美结耦,这个是一个遗憾
- 性能方面,
mobx
的缓存性能优化依赖immutable
所以在使用友好性方面较于natur
更好, 但是natur
也有着模块懒加载这样的功能优于mobx
# 使用注意事项
由于低版本不支持react.forwardRef方法,所以不能直接使用ref获取包裹的组件实例,需要使用forwardedRef属性获取(用法同ref)
在TypeScript中的提示可能不那么友好,比如
@inject('count', 'name')
class App extends React.Component {
// ...
}
// 此使用方法会报错,提示App组件中无forwardedRef属性声明
<App forwardedRef={console.log} />
// 以下使用方式则不会报错
class _App extends React.Component {
// ...
}
const App = inject('count', 'name')(_App);
// 正确
<App forwardedRef={console.log} />
- 在actions中修改state,需要遵循immutable规范