Github Mobx Observable – an-Object
This article is the first in a series of mobx source code readings
This series uses the newer mobx version, V5.13.0
Technical premises
Before reading, you are expected to have some knowledge or practice of the following techniques, otherwise it may affect your understanding of this article
-
ES6 decorator: decorator
-
ES6 proxy: proxy
-
ES6 Reflect: Reflect
-
Define Object attributes: Object.defineProperty
-
Implement the simple observer mode
-
Implementing a simplified VERSION of MVVM (optional)
To prepare
1. Directory structure
├─ SRC │ ├─ API// The API for the hijacking method and the reaction│ ├ ─ ─ the core// Global state, functions, etc│ ├ ─ ─ types// Rewrite the API for object, array, etc│ ├ ─ ─ utils// Tool methods│ │ ├ ─ ─ internal. Ts └ ─ ─ mobx. Ts// Global export└ ─ ─ package. JsonCopy the code
Two, hijacking principle
Two key words: attribute hijacking, recursion
-
Attribute hijacking, which can be understood as programming at the programming language level, is particularly evident in Proxy & Reflect
-
Object.defineproperty or proxy allows you to do some extra things when retrieving or modifying Object attributes
-
Mobx5 version of the default hijacking reconstruction for proxy, mainly because proxy is more stable than Object. DefineProperty, and more hijacking means, not to expand the specific
-
The property value of an object may also be an object or an array, so it is recursively hijacked if it is a reference type
Three, the overall steps (this article only discusses the hijacking object)
Have a general understanding of the overall steps first, to facilitate the understanding behind
- Process the decorator and prepare the parameters needed for the decorator
Decorators are written with and without parentheses, but they all end up requiring a return descriptor
@observable obj …
@log({ name: ‘lawler’ }) obj2 …
- Hijacks properties of the current layer of an object
Depending on the type of the attribute value, the corresponding enhancer of the call (that is, the hijacker, as described below) is hijacked
- Recursive hijacked
Determines if it is a reference type, and if so, recursively hijack
- Expose manipulation apis to facilitate user operations, such as Mobx keys, values, and set
On the source code
Source code screenshots have a lot of corresponding code comments, please also read together
1. Observable definition
- Find an Observable (SRC/API/Observable.ts) from mobx.ts
Observable is assigned by createObservable
And iterate over the keys of observableFactories, attaching the properties to the Observable (and createObservable).
- CreateObservable hijacked device
Using the object, array, and other properties of just observableFactories, call the corresponding method hijacking according to the variable type
Notice that Observable and createObservable are one object and call each other, so watch out for this bend
- The key see observables. Object, namely observableFactories. Object
The object function takes three arguments, the third of which is options to customize the hijacking method
const person = observable({
name: 'lawler',
get labelText() {
return this.showAge ? `The ${this.name} (age: The ${this.age}) ` : this.name; }, setAge(age) { his.age = age; }}, {// This is the second parameter decorators
setAge: action
} /* pass the third options parameter */);
Copy the code
- AsCreateObservableOptions treatment Options
If you don’t pass the options to return to the default defaultCreateObservableOptions, if pass the options is written according to the user
Going back to step 3, Object. DefineProperty hijacking is used if O.proxy is false, proxy hijacking is used otherwise
- According to the options for the type of the decorator to the corresponding processing, see getDefaultDecoratorFromObjectOptions do
Options. deep defaults to true, so deepDecorator is taken by default
DeepDecorator from createDecoratorForEnhancer (this is the key of the back, put it) first, need to pass a parameter enhancer
DeepEnhancer if you need a deepDecorator; If you need a shallowDecorator, pass shallowEnhancer…
DeepEnhancer is actually a hijacking method that calls different observable parameters, such as Object and array, according to different types of variables
Deja vu looks something like the createObservable in Step 2
Now, keep in mind that enhancer is a hiker that provides methods to hijack various types (the equivalent of an Observable).
- Next, an empty object is passed inside the extendObservable to initialize the new product property
So @Observable obj… It’s not going to change the original object
If the properties is not empty (i.e. Object. DefineProperty hijack) directly call extendObservableObjectWithProperties
If go proxy hijacked, after get to proxy objects (const proxy = createDynamicObservableObject (base), take the initiative to call extendObservableObjectWithProperties. See Section 1, Step 3
So you can see that the main purpose of this function is to initialize the properties of the target product in different hijacking cases: initializeInstance (remember this function, later) and asObservableObject
- The current layer with different parameters are ready, hijacked, look at how extendObservableObjectWithProperties
It goes without saying that
Get the decorator ready to process the object, then pass in the three basic parameters required by the property decorator: target, key, descriptor
The return result is a hijacked resultDescriptor, which is then written to target (the base empty Object that is propped by proxy) via Object.defineProperty
This completes the hijacking of the current key of the current layer
Decorator god building tool
Check out the codesandBox: decorator demo
- Examine the source of the decorator
After reading Chapter 1 step 5 you may be wondering, how do you get any of my current decorators, any data types, and any custom hijacked decorator functions
Back there now, before the seeds: createDecoratorForEnhancer
We start from getDefaultDecoratorFromObjectOptions, inside by calling createDecoratorForEnhancer and afferent deepEnhancer deepDecorator for our use
Now look at how createDecoratorForEnhancer generate various decorator. This is a very difficult point, please read it again and again patiently
- CreateDecoratorForEnhancer god tools
First, call createPropDecorator to get the decorator, declare a variable res, hang enhancer below it, and return the RES
CreatePropDecorator passes two arguments, the first a Boolean and the second a function
This function is a decorator agent function, in order to get enhancer on createDecoratorForEnhancer level
- Go inside the createPropDecorator and see how you can use this. Okay
Function parameters
the
As you can see, the whole thing is a function that creates a decoratorFactory factory, essentially returning the corresponding factory, depending on the enhancer
The main thing in decoratorFactory is to unify the use of @decorator obj and @decorator(‘decoratorArguments’) obj2
The results of the decorator function returns the createPropertyInitializerDescriptor, its specific return is a descriptor
Again, the decorator function executes and returns a Descriptor, which is exactly what we need, as shown in Section 1, Step 7
At the end of the decoratorFactory, use the quacksLikeADecorator to determine what type the decorator is
If @decorator obj, direct decorator return descriptor (decorator.apply(null, arguments as any))
Returns decorator(executed at writing time) if @decorator(‘decoratorArguments’) obj2
It is worth remembering that the arguments passed in the second way are the arguments object of the decoratorFactory, so why quacksLikeADecorator uses arguments
- See what createPropertyInitializerDescriptor returned to what
If you finally see the familiar get and set function that calls initializeInstance, remember the function described in Chapter 1, step 6
In addition to adding addHiddenProp, this method calls propertyCreator, which is the second parameter passed in the createPropDecorator in Step 3 of Chapter 2. Then put in target[mobxPendingDecorators]! [prop] property for extendObservable
As a reminder, the entire chapter 2 is built on step 5 of Chapter 1
InitializeInstance is called when the base empty object is initialized and when the object is manipulated
- What does propertyCreator do, go back
Chapter 2 Step 2
Look at the second parameter passed by createPropDecorator
You can see that a function is called: asObservableObject, which is passed in as an attribute value of the original object, and addObservableProp is chain-called
We can all guess what this function is doing by assigning the hijacked initialValue to the propertyName property via enhancer
AsObservableObject Object manager
- How does asObservableObject manage objects
Through the target (is a decorator modified target, as the entire object) to the object of ObservableObjectAdministration (if the object attribute value is the attribute values will also have adm)
And hang it under target’s $mobx property for later exposure APIS
Call the addObservableProp method when you get the object manager and hijack the current propertyName of the current layer of the object
- After ObservableObjectAdministration manager (adm)
You can see that ADM is also a wrapper class, which revolves around Values, which is a Map with PropertyKey and ObservableValue
Methods like Read, write, etc., are finally called by the ObservableValue API
- How does addObservableProp populate ObservableValue
The new ObservableValue is passed to the newValue and enhancer to get the hijacked Observable
Then fill in this.values, and adm manages all subsequent operations
- How does ObservableValue hijack
You can see that the ObservableValue is expanded around the value, hijacked by the enhancer, where the enhancer is actually used
Here, if the hijacked property value is also an object, enhancer hijacking is called, which then recurses all previous steps
In addition, ObservableValue provides apis for GET, set methods, and Objects, such as toString
Finally under the comb back in chapter 2 step 4: return to get and set after createPropertyInitializerDescriptor execution and they all call initializeInstance inside
InitializeInstance calls the cached propertyCreator, which uses the asObservableObject to fetch the ADM for various operations
- Set method enhances hijacking
Remember when we changed an Observable, it was still hijacked
@observable obj = { a: 1 };
obj = { b: 2, c: { d: 3 } };
It’s just hijacking the new value and then assigning it
4. Expose the API
We know that adm is the core of the hijacked Object, so it is possible to do all kinds of operations once we get an ADM
This is done by caching $mobx in Step 1 of Chapter 3, and the problem is solved
- keys
- set
The last
-
Annotated mobx source code
-
Welcome to discuss in the Mobx source code Interpretation issue
-
Recommended: Minbx: Mini Mobx for learning to use, welcome pr: Minbx project address
-
Code word is not easy, like to remember ❤️ oh