preface
I found the source code for Mobx in Git and found that it is written in TypeScript. Since I have no experience with Typescrit, I will compile it into JavaScript first, so we can either run the following script or download a compiled source directly from CDN. We can choose the UMD specification script:
git clone [email protected]:mobxjs/mobx.git
npm i
npm run quick-build
I downloaded a source code directly from CDN and analyzed it.
Demo
Let’s start with a basic Demo to see how Mobx can be used:
const addBtn = document.getElementById('add')
const minusBtn = document.getElementById('minus')
const incomeLabel = document.getElementById('incomeLabel')
const bankUser = mobx.observable({
name: 'Ivan Fan',
income: 3,
debit: 2
});
const incomeDisposer = mobx.autorun(() => {
incomeLabel.innerText = `Ivan Fan income is ${bankUser.income}`
})
addBtn.addEventListener('click', ()=> {
bankUser.income ++
})
minusBtn.addEventListener('click', () => {bankuser.income --}) copy the codeCopy the code
Our interface is very simple, as shown below:
Two buttons and one label. In the js file, we add a click event to the two buttons. The main body of the event is very simple: ‘bankUser. Income ++’ ‘bankUser. Income –‘, which is to increase or decrease the income attribute of ‘bankUser’. The content of the label in the middle has changed. However, we don’t manipulate the contents of **incomeLabel** in the Button click event, but the contents do change in real time with the click event. **incomeLabel** text: “` const incomeDisposer = mobx.autorun(() => { incomeLabel.innerText = `Ivan Fan income is ${bankUser.income}` }) “` This is the simplest and most mysterious feature of **Mobx**, and we can delve into it from here.
observable
From the JS file above, we can see that two mobx methods are referenced, respectively
和
autorunYes, there are two ways to let
incomeLabelChanges occur in real time when the button is clicked, so we will conduct in-depth analysis of these two methods in the next chapter, which will be analyzed first
Let’s do the analysis. Let’s open Mobx’s first
The source codeIf we use
VscodeOpen the source code, we can use the shortcut key
Ctrl + K
Ctrl + 0
Fold it all up, open it up, and find
exportsTo see which methods mobx exposes:
Exposes a series of methods that we will use later.
Observable (const bankUser = mobx.observable({make a breakpoint on this line, F11, jump in, and find that the source code corresponds to one
Method, that is, to create an observable object:
var observable$The $1 = createObservable;
function createObservable(v, arg2, arg3) {
if (typeof arguments[1] === "string") {
return deepDecorator$The $1.apply(null, arguments);
}
if (isObservable$The $1(v))
return v;
var res = isPlainObject$The $1(v)
? observable$The $1.object(v, arg2, arg3)
: Array.isArray(v)
? observable$The $1.array(v, arg2)
: isES6Map$The $1(v)
? observable$The $1.map(v, arg2)
: v;
if(res ! == v)return res;
// otherwise, just box it
fail$The $1(process.env.NODE_ENV ! = ="production" &&
"The provided value could not be converted into an observable. If you want just create an observable reference to the object use 'observable.box(value)'"); } Duplicate codeCopy the code
The above code is simple, with three arguments, but when we call it, the value passes one argument, so we only care about the first argument for the moment
Here is the basic logic of this functin:
- If the second argument passed in is a string, call the deepDecorator directly? 1.apply(null, arguments);
- Check if the first argument is already an observable, and return the observable if it is
- Determine what type the first argument is and then call different methods. There are three types in total:
object
.
array.
map(ES Map data type), respectively:
observable? 1.object
.observable? 1.array
.observable? 1.map
Method, what about this Observable? What is 1? In the first rowvar observable? 1 = createObservable;
The surface is the createObservable method. But this method is only a few lines of code, there are no object, array, map methods, we found under this methodobservableFactoriesObject, a factory object that adds methods to createObservable, which defines all three methods and traverses themObject.keys(observableFactories).forEach(function (name) { return (observable? 1[name] = observableFactories[name]); });
Since we’re passing an Object in our Demo, we call Observable, right? 1. Object method, next we continue to analyze this method, its code is as follows:
object: function (props, decorators, options) {
if (typeof arguments[1] === "string")
incorrectlyUsedAsDecorator("object");
var o = asCreateObservableOptions$The $1(options);
if (o.proxy === false) {
return extendObservable$The $1({}, props, decorators, o);
}
else {
var defaultDecorator = getDefaultDecoratorFromObjectOptions$The $1(o);
var base = extendObservable$The $1({}, undefined, undefined, o);
var proxy = createDynamicObservableObject$The $1(base);
extendObservableObjectWithProperties$The $1(proxy, props, decorators, defaultDecorator);
returnproxy; }}, copy the codeCopy the code
var o = asCreateObservableOptions? 1(options); A simple object is generated:
var defaultCreateObservableOptions$The $1 = {
deep: true,
name: undefined,
defaultDecorator: undefined,
proxy: true}; Copy the codeCopy the code
The value of O.proxy is true, so the else branch will be followed, so we will analyze each piece of code in the else branch.
var defaultDecorator = getDefaultDecoratorFromObjectOptions? 1(o);
This is the decorator logic that we’ll skipvar base = extendObservable? 1({}, undefined, undefined, o);
对oThe object is processed to become one
Symbol
Data type.
This is an important step, adding a $mobx? To an empty object. 1(var $mobx? 1 = Symbol(“mobx administration”);) Properties, its value is a ObservableObjectAdministration type object, the write method will call in the subsequent data interception.
var proxy = createDynamicObservableObject? 1(base);
This method, the mostThe core, this object is proxied (Proxy)
The get, set, HAS, deleteProperty, ownKeys, preventExtensions methods on the properties of this object are proxyed, which is the core point for Mobx event data to be added.
-
The third proxy initializes a simple proxy object, but does not associate it with the observable target passed in by the mobx. Observable method. extendObservableObjectWithProperties? 1(proxy, props, decorators, defaultDecorator); Method iterates over the target property and assigns it to the proxy object. Then all objects in mobx. Observable are proxied, which intercepts property operations.
-
In the first four extendObservableObjectWithProperties? Method 1, which eventually decorates the properties of the original object by looking at function’s Call stack, and then calling
ObservableObjectAdministrationThe addObservableProp method for eachpropName(Key of the original object) generates one
ObservableValueObject and is saved in
ObservableObjectAdministrationThe object’svaluesIn the
Figure 3 shows that the real data interception is the objectProxyTraps interceptor. In the next chapter, we need to conduct an in-depth analysis of this interceptor, focusing on how get and set implement data interception.
return proxy;
An already proxied object is eventually returned, replacing the native object.
The bankUser object is an already proxied object and contains a new property of type Symbol.
const bankUser = mobx.observable({
name: 'Ivan Fan', income: 3, debit: 2 }); Copy the codeCopy the code
conclusion
- An Observable first passes in a raw object (it can pass in various types of data:
array
.map
.object
For now, only object cases are analyzed. - Create an empty Object and add some default properties (
var base = extendObservable? 1({}, undefined, undefined, o);
), including oneSymbol
Type, whose value is aObservableObjectAdministration
Object of type. - Use this objectES6 的
Proxy
Proxying intercepts a list of operations on this object (
get
.set
…).var proxy = new Proxy(base, objectProxyTraps);
- Iterate over the original object, attaching all of its own properties to the newly created empty object
- Returns the processed object
bankUser
- You can then listen for the corresponding operations on this object.
- The object after processing is shown in the figure below, and the object after operation is the object below, butobservableMethod, in fact, only the second step (2), the third step (3) of the following figure
observers
Property is also a Set object without any values, which will be examined laterautorunMethod involves when to assign a value to it