1. mobx
observable用来声明可观察的数据reaction对 observables变化 作出响应autorun在执行函数使用的数据变化执行action动作是任何用来修改状态的东西
const todos = observable([
{
title: "Make coffee",
done: true,
},
{
title: "Find biscuit",
done: false
}
]);
const reaction = reaction(
() => todos.map(todo => todo.title),
titles => console.log("reaction :", titles.join(", "))
);
const autorun1 = autorun(
() => console.log("autorun:", todos.map(todo => todo.title).join(","))
);
todos.push({ title: "explain reactions", done: false });
// 输出:
// reaction: Make coffee, find biscuit, explain reactions
// autorun: Make coffee, find biscuit, explain reactions
2. react-mobx
Provider以组件的形式存在,用来包裹最外层组件节点,并且传入store通过context传递给后代组件。observer装饰的react组件将转换成一个监听者,当observable修饰的数据变化,react组件就会重新渲染。inject为了使被装饰的组件以props的形式获取到Provider传递过来的数据。
创建store
import { observable, action } from 'mobx'; // 引入mobx 提供的方法
class baseStore {
@observable name = "";
@action changeName = (name)=>{
this.name = name;
}
}
export default new baseStore ();
注入store
import ReactDOM from 'react-dom';
import {Provider} from 'mobx-react';
const stores = {
baseStore,
};
ReactDOM.render(
<Provider {...stores}>
<Page />
</Provider>
,document.getElementById('root')
);
使用store
import { observer, inject } from 'mobx-react';
@inject('baseStore')
@observer
export default class Home extends Component {
constructor(props) {
super(props);
this.state = { };
}
componentDidMount() {
console.log(this.props.baseStore)
}
changeName(){
this.props.baseStore.changeName("hello");
}
render(){
var { name } = this.props.baseStore;
return (
<>
<div>name:{name}</div>
<button onClick={()=>{this.changeName()}}>更改name</button>
</>
)
}
}
3 响应式原理
mobx整体是一个观察者模式的架构,存储 state 的 store 是被观察者,使用 store 的组件是观察者。
Provider 通过context传递store;
inject 注入依赖的store;
observer(autorun.run)包裹render函数,依赖数据发生变化触发dom更新;
4 mobx-react 源码
-
observer
observer函数/装饰器可以用来将 React 组件转换成响应式组件。
它用mobx.autorun包装了组件的 render 函数以确保任何组件在渲染中使用的数据变化时都可以强制刷新组件。observer是由单独的mobx-react包提供的 -
Provider
var MobXProviderContext =
/*#__PURE__*/
React__default.createContext({});
function Provider(props) {
var children = props.children,
stores = _objectWithoutPropertiesLoose(props, ["children"]);
var parentValue = React__default.useContext(MobXProviderContext);
var mutableProviderRef = React__default.useRef(_extends({}, parentValue, {}, stores));
var value = mutableProviderRef.current;
if (process.env.NODE_ENV !== "production") {
var newValue = _extends({}, value, {}, stores);
}
return React__default.createElement(MobXProviderContext.Provider, {
value: value
}, children);
}
Provider.displayName = "MobXProvider";
- inject
核心代码createStoreInjector
function inject() {
for (var _len = arguments.length, storeNames = new Array(_len), _key = 0; _key < _len; _key++) {
storeNames[_key] = arguments[_key];
}
if (typeof arguments[0] === "function") {
var grabStoresFn = arguments[0];
return function (componentClass) {
return createStoreInjector(grabStoresFn, componentClass, grabStoresFn.name, true);
};
} else {
return function (componentClass) {
return createStoreInjector(grabStoresByName(storeNames), componentClass, storeNames.join("-"), false);
};
}
}
createStoreInjector函数使用forwardRef钩子返回一个新组件;
并将接受到的ref以及 useContext 获取的store通过 props注入到 @inject修饰的类组件中
function createStoreInjector(grabStoresFn, component, injectNames, makeReactive) {
// React.forwardRef 用于转发ref,并返回一个新组件
var Injector = React__default.forwardRef(function (props, ref) {
var newProps = _extends({}, props);
var context = React__default.useContext(MobXProviderContext);
Object.assign(newProps, grabStoresFn(context || {}, newProps) || {});
if (ref) {
newProps.ref = ref;
}
return React__default.createElement(component, newProps);
});
if (makeReactive) Injector = observer(Injector);
Injector["isMobxInjector"] = true;
copyStaticProperties(component, Injector);
Injector["wrappedComponent"] = component;
Injector.displayName = getInjectName(component, injectNames);
return Injector;
}
参考 juejin.cn/post/690871… segmentfault.com/a/119000002…
欢迎关注我的前端自检清单,我和你一起成长