这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战”
前言
使用了mobx
库很长时间,一直想探寻一下内部更新的原理正好最近有时间阅读了mobx
部分api
的源码,今天来分享一下内部的原理看看是怎么做到和react
视图连接的,是怎么做到更新之后能响应到视图上的
开始
今天主要会实现一下
mobx
和mobx-react
中的一些核心api
1、observable
来自于mobx
中,其中主要原理是使用Object.defineProperty
,这一点和Vue2
的响应式系统是很相似
2、observer
来自于mobx-react
中,在mobx-react-lite
中也有observer
但是只适用于hooks
组件,而mobx-react-lite
也是基于mobx-react
,所以今天只实现mobx-react
中的observer
因为,这个在class
类和hooks
中是通用的
准备工作
1.需要使用creat-react-app
生成一个脚手架(这里百度就可以),进行我们的案例实验
2.创建一个List.jsx
,用于展示我们的结果页面很简单,最后做到可以初始化响应式变量,并且可以顺利更新
// import { observer } from 'mobx-react'
import Store from './Store/store'
import { useObserver, observerLite, observer } from './lhc-mobx-react'
const List = observer(() => {
return <>
点赞量:{Store.count}
<button onClick={Store.setCount}>更新点赞量</button>
</>
})
export default List
复制代码
如果你不想自己试一下那我可以给你截个图看看页面效果
3.创建一个Store.js
,用来存储数据
// import { observable, action, configure } from 'mobx'
import { observable } from '../lhc-mobx'
const state = observable({
count: 1
})
state.setCount = action(() => {
state.count++
})
export default state
复制代码
上代码
mobx-react和react视图连接只要有三种方式,useObserver、observer、Observer
1.useObserver
// import { observer } from 'mobx-react'
import Store from './Store/store'
import { useObserver, observer } from './lhc-mobx-react'
const List = () => {
return useObserver(()=><>
点赞量:{Store.count}
<button onClick={Store.setCount}>更新点赞量</button>
</>)
}
export default List
复制代码
2.observer
// import { observer } from 'mobx-react'
import Store from './Store/store'
import { useObserver, observer } from './lhc-mobx-react'
const List = observer(() => {
return <>
点赞量:{Store.count}
<button onClick={Store.setCount}>更新点赞量</button>
</>
})
export default List
复制代码
3.Observer
使用组件的方式进行包装
const List = () => {
return <Observer>
{()=> <>
点赞量:{Store.count}
<button onClick={Store.setCount}>更新点赞量</button></>}
</Observer>
}
export default List
复制代码
提示:接下来我们依次实现这三种方式,因为已经是写完验证过的了,所以在实现完成的过程中不会在进行校验,并且只是实现基础更新初始化功能(毕竟源码这么多😂),大部分注释都会写在代码中,如果哪里有问题可以在评论区讨论
新建一个lhc-mobx-react.js
文件
useObserver实现
import React, { useRef, useReducer, useEffect, memo, forwardRef, Component } from 'react'
import { Reaction } from 'mobx'
function observerComponentNameFor(component) {
return `&observer${component}`
}
/**
* @fn 执行的回调
* @baseComponentName 组件标识
* @options 其他操作
*/
function useObserver(fn, baseComponentName = 'observed', options = {}) {
//创建强制更新api
const [, forceUpdate] = useReducer(x => x + 1, 0)
//创建ref存储reaction
const reactionTrackRef = useRef(null)
if (!reactionTrackRef.current) {
reactionTrackRef.current = {
reaction: new Reaction(
observerComponentNameFor(baseComponentName),
() => {
// 监测到数据更新的时候会执行
forceUpdate()
}
)
}
}
const { reaction } = reactionTrackRef.current
let rendering = null
reaction.track(() => {
//监听更新重新执行传进来的方法
rendering = fn()
})
useEffect(() => {
return () => {
//组件执行完毕进行销毁
reaction.dispose()
reactionTrackRef.current = null
}
}, [])
return rendering
}
export {
useObserver
}
复制代码
observer实现用到了几个方法
observer
function observer(component, options) {
// 处理forwardRef
if (component["$$typeof"] === Symbol.for("react.forward_ref")) {
const baseRender = component["render"];
return React.forwardRef(function () {
const args = arguments;
//这里使用的Observer就是上面所说的第三种实现方式在下面会展示
return <Observer>{() => baseRender.apply(undefined, args)}</Observer>;
});
}
if (
// 处理函数组件逻辑
(typeof component === "function" && !component.prototype) ||
!component.prototype.render
) {
// observerLite是mobx-react-lite中的observer,怕命名冲突改成了observerLite
return observerLite(component, options);
}
// 处理类组件
return makeClassComponentObserver(component);
}
export {
useObserver,
observer
}
复制代码
observerLite
function observerLite(baseComponent, options = {}) {
let realOptions = {
forwardRef: false,
...options
};
const useWrappedComponent = (props, ref) => {
// 将baseComponent和数据连接
return useObserver(() => baseComponent(props, ref));
};
let memoComponent;
if (realOptions.forwardRef) {
//处理forwardRef
memoComponent = memo(forwardRef(useWrappedComponent));
} else {
//mobx-react-lite内部的observer默认有React.memo行为,在这里加上
memoComponent = memo(useWrappedComponent);
}
return memoComponent;
}
复制代码
makeClassComponentObserver
function makeClassComponentObserver(componentClass) {
const target = componentClass.prototype;
//获取class类的虚拟dom
const baseRender = target.render;
target.render = function () {
//处理class类中的render
return makeComponentReactive.call(this, baseRender);
};
return componentClass;
}
复制代码
makeComponentReactive
function makeComponentReactive(render) {
const baseRender = render.bind(this);
let isRenderingPending = false;
//监听render
const reaction = new Reaction(`${this.constructor.name}.render`, () => {
if (!isRenderingPending) {
isRenderingPending = true;
//监测到数据变化强制更新视图
Component.prototype.forceUpdate.call(this);
}
});
this.render = reactiveRender;
function reactiveRender() {
isRenderingPending = false;
let rendering = undefined;
//更新render
reaction.track(() => {
rendering = baseRender();
});
return rendering;
}
return reactiveRender.call(this);
}
export {
useObserver,
observer,
Observer
}
复制代码
Observer实现
function Observer({children, render}) {
const component = children || render;
return useObserver(component);
}
复制代码
mobx
中的observable
创建一个响应式数据、action
用来更新数据,使用过程可以用@装饰器的方法,也可以直接使用函数式的方法现在来实现一下
先看几个工具函数,之后在编写observable和action过程中会用到
创建utils.js
const plainObjectString = Object.toString()
const objectPrototype = Object.defineProperty
export const isObject = (value) => {
return value !== null && typeof value === 'object'
}
export const isPlaneObject = (value) => {
if (!isObject(value)) return false
const proto = Object.getPrototypeOf(value)
if (proto === null) return true
return proto.constructor.toString() === plainObjectString
}
export const hasProp = (target, prop) => {
return objectPrototype.hasOwnProperty.call(target, prop)
}
export const $mobx = Symbol('mobx administration')
export const addHiddenProp = (object, propName, value) => {
Object.defineProperty(object, propName, {
value,
enumerable: false,
writable: true,
configurable: true,
})
}
export const getDescriptor = Object.getOwnPropertyDescriptor
复制代码
observable
import { isPlaneObject, hasProp, $mobx, addHiddenProp } from './utils'
const createObservable = (v) => {
//判断是否是对象的方式
if (isPlaneObject(v)) {
return observable.object(v)
}
}
const observableFactories = {
object: props => {
return extendObservable({}, props)
}
}
export const observable = Object.assign(createObservable, observableFactories)
复制代码
extendObservable
//添加响应式对象
const extendObservable = (target, props) => {
//将target变为可观察模式 在返回
const adm = asObservableObject(target)
//遍历所有属性变为响应式
Object.keys(props).forEach(key => {
adm._addObservableProp(key, props[key])
})
return target
}
复制代码
asObservableObject
const asObservableObject = (target) => {
if (hasProp(target, $mobx)) {
//如果已经添加过这个属性直接返回
return target[$mobx]
}
const adm = new ObserverableObjectAdministration(target)
//调用工具函数变为响应式
addHiddenProp(target, $mobx, adm)
return adm
}
复制代码
ObserverableObjectAdministration
class ObserverableObjectAdministration {
constructor(_target) {
this._target = _target
}
//添加属性
_addObservableProp(propName, newValue) {
this._target[propName] = newValue
}
}
复制代码
到这里核心源码就已经实现的差不多了,之后会在更新文章继续实现
action
的原理,最后提前祝大家国庆节快乐☕️