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

  1. ES6 decorator: decorator

  2. ES6 proxy: proxy

  3. ES6 Reflect: Reflect

  4. Define Object attributes: Object.defineProperty

  5. Implement the simple observer mode

  6. 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

  1. Attribute hijacking, which can be understood as programming at the programming language level, is particularly evident in Proxy & Reflect

  2. Object.defineproperty or proxy allows you to do some extra things when retrieving or modifying Object attributes

  3. 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

  4. 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

  1. 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 …

  1. 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

  1. Recursive hijacked

Determines if it is a reference type, and if so, recursively hijack

  1. 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

  1. 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).

  1. 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

  1. 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

  1. 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

  1. 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).

  1. 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

  1. 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

  1. 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

  1. 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

  1. Go inside the createPropDecorator and see how you can use this. OkayFunction parametersthe

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

  1. 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

  1. What does propertyCreator do, go backChapter 2 Step 2Look 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

  1. 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

  1. 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

  1. 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

  1. 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

  1. 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

  1. keys

  1. set

The last

  1. Annotated mobx source code

  2. Welcome to discuss in the Mobx source code Interpretation issue

  3. Recommended: Minbx: Mini Mobx for learning to use, welcome pr: Minbx project address

  4. Code word is not easy, like to remember ❤️ oh