Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Case Analysis of Javascript Framework

2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

Today, the editor will share with you the relevant knowledge points of Javascript framework case analysis, the content is detailed and the logic is clear. I believe most people still know too much about this knowledge, so share this article for your reference. I hope you can get something after reading this article. Let's take a look at it.

All the code in this article is based on Hongmeng's current latest version (version 677ed06, submission date is 2020-09-10).

Hongmeng system uses Javascript to develop GUI, which is similar to WeChat Mini Programs and light application mode. In this MVVM model, V is actually undertaken by C++. The Javascript code is just the ViewModel layer.

The Hongmeng JS framework is zero-dependent, and only some npm packages are used in the development and packaging process. The packaged code does not rely on any npm packages. Let's first take a look at what the JS code written using the Hongmeng JS framework looks like.

Export default {data: {return {count: 1};}, increase () {+ + this.count;}, decrease () {--this.count;},}

If I don't tell you it's Hongmeng, you'll even think it's vue or Mini Program. If you take the JS out and use it separately (out of the Hongmeng system), the code goes like this:

Const vm = new ViewModel ({data () {return {count: 1};}, increase () {+ + this.count;}, decrease () {--this.count;},}); console.log (vm.count); / / 1vm.increase (); console.log (vm.count); / / 2vm.decrease (); console.log (vm.count); / / 1

All the JS code in the repository implements a responsive system that acts as the ViewModel in MVVM.

Let's analyze it line by line.

There are 4 directories in the src directory, with a total of 8 files. One of them is a unit test. There is also a performance analysis. Excluding 2 index.js files, there are 4 useful files in total. It is also the focus of this paper.

Src ├── _ _ test__ │ └── index.test.js ├── core │ └── index.js ├── index.js ├── observer │ ├── index.js │ ├── observer.js │ ├── subject.js │ └── utils.js └── profiler └── index.js

The first is the entry file, src/index.js, with only 2 lines of code:

Import {ViewModel} from'. / core';export default ViewModel

It's actually re-export. Another similar file is src/observer/index.js, which is also 2 lines of code:

Export {Observer} from'. / observer';export {Subject} from'. / subject'

Observer and subject implement an observer pattern. Subject is the subject, that is, the observed. Observer is the observer. The observer needs to be notified actively when there is any change in the subject. This is the response style.

Both files use src/observer/utils.js, so let's analyze the utils file first. It is divided into three parts.

The first part export const ObserverStack = {stack: [], push (observer) {this.stack.push (observer);}, pop () {return this.stack.pop ();}, top () {return this.stack [this.stack.length-1];}}

The first is to define a stack used to store observers, following the last-in-first-out principle, internally using the stack array to store.

The stack operation push, like the push function of the array, places an observer observer at the top of the stack.

The unstack operation pop, like the pop function of the array, deletes the observer at the top of the stack and returns the deleted observer.

Taking the top element top, unlike the pop operation, top takes the top element out of the stack, but does not delete it.

The second part export const SYMBOL_OBSERVABLE ='_ _ ob__';export const canObserve = target = > typeof target = = 'object'

Defines a string constant SYMBOL_OBSERVABLE. For convenience in the back.

Defines a function canObserve, whether the target can be observed. Only objects can be observed, so use typeof to determine the type of target. Wait, there seems to be something wrong. If target is null, the function also returns true. If the null is not observable, then this is a bug. In writing this article, I have already mentioned a PR and asked if this behavior is the desired behavior.

The third part export const defineProp = (target, key, value) = > {Object.defineProperty (target, key, {enumerable: false, value});}

There's nothing to explain, but the Object.defineProperty code is too long, so define a function to avoid code duplication. Next, let's analyze the observer src/observer/observer.js, which is divided into four parts.

The first part

Export function Observer (context, getter, callback, meta) {this._ctx = context; this._getter = getter; this._fn = callback; this._meta = meta; this._lastValue = this._get ();}

Constructor. Accept 4 parameters.

The morning and afternoon of the current observer of context, type is ViewModel. When the third parameter, callback, is called, the this of the function is this context.

The getter type is a function that gets the value of an attribute.

The callback type is a function that is a callback function executed when a value changes.

Meta metadata. The Observer does not focus on meta metadata.

On the last line of the constructor, this._lastValue = this._get (). Let's analyze the _ get function.

Part II

Observer.prototype._get = function () {try {ObserverStack.push (this); return this._getter.call (this._ctx);} finally {ObserverStack.pop ();}}

ObserverStack is the stack analyzed above to store all observers. Puts the current observer on the stack and gets the current value through _ getter. Combined with the constructor in the first part, this value is stored in the _ lastValue attribute. After performing this process, the observer is initialized.

Part III

Observer.prototype.update = function () {const lastValue = this._lastValue; const nextValue = this._get (); const context = this._ctx; const meta = this._meta; if (nextValue! = = lastValue | | canObserve (nextValue)) {this._fn.call (context, nextValue, lastValue, meta); this._lastValue = nextValue;}}

This part implements the mechanism of dirty check (Dirty checking) when data is updated. Compare the updated value with the current value, and if it is different, execute the callback function. If the callback function is to render UI, then you can render on demand. If the value is the same, then check whether the new value set can be observed, and then decide whether or not to execute the callback function.

Part IV: Observer.prototype.subscribe = function (subject, key) {const detach = subject.attach (key, this); if (typeof detach! = = 'function') {return;} if (! this._detaches) {this._detaches = [];} this._detaches.push (detach);}; Observer.prototype.unsubscribe = function () {const detaches = this._detaches; if (! detaches) {return } while (detaches.length) {detaches.pop () ();}}

Subscribe and unsubscribe.

We used to talk about the observer and the observed. In fact, there is another term for the observer mode, which is called the subscription / publish model. This part of the code implements subscriptions to topics (subject).

First call the attach method of the topic to subscribe. If the subscription is successful, the subject.attach method returns a function that is unsubscribed when called. In order to unsubscribe in the future, this return value must be saved.

Many people should have guessed the implementation of subject. The observer subscribes to subject, so all subject needs to do is notify the observer when the data changes. How does subject know that the data has changed? the mechanism is the same as vue2, using Object.defineProperty to do property hijacking.

Next, let's analyze the observer src/observer/subject.js, which is divided into seven parts.

The first part

Export function Subject (target) {const subject = this; subject._hijacking = true; defineProp (target, SYMBOL_OBSERVABLE, subject); if (Array.isArray (target)) {hijackArray (target);} Object.keys (target) .forEach (key = > hijack (target, key, target [key]);}

Constructor. There are almost no difficulties. Set the _ hijacking property to true to indicate that the object has been hijacked. Object.keys hijacks each property through history. If it is an array, hijackArray is called.

The second part is about two static methods.

Subject.of = function (target) {if (! target | |! canObserve (target)) {return target;} if (target [SYMBOL _ OBSERVABLE]) {return target [SYMBOL _ OBSERVABLE];} return new Subject (target);}; Subject.is = function (target) {return target & target._hijacking;}

The constructor of Subject is not called directly from the outside, but is encapsulated in a Subject.of static method.

If the target cannot be observed, return directly to the target.

If the target [SYMBOL _ OBSERVABLE] is not undefined, the target has already been initialized.

Otherwise, the constructor is called to initialize the Subject.

Subject.is is used to determine whether the target has been hijacked.

Part III

Subject.prototype.attach = function (key, observer) {if (typeof key = 'undefined' | |! observer) {return;} if (! this._obsMap) {this._obsMap = {};} if (! this._ obsmap [key]) {this._ obsmap [key];} const observers = this._ obsmap [key]; if (observers.indexOf (observer)

< 0) { observers.push(observer); return function() { observers.splice(observers.indexOf(observer), 1); }; }}; 这个方法很眼熟,对,就是上文的 Observer.prototype.subscribe 中调用的。作用是某个观察者用来订阅主题。而这个方法则是"主题是怎么订阅的"。 观察者维护这一个主题的哈希表 _obsMap。哈希表的 key 是需要订阅的 key。比如某个观察者订阅了 name 属性的变化,而另一个观察者订阅了 age 属性的变化。而且属性的变化还可以被多个观察者同时订阅,因此哈希表存储的值是一个数组,数据的每个元素都是一个观察者。 第四部分 Subject.prototype.notify = function(key) { if ( typeof key === 'undefined' || !this._obsMap || !this._obsMap[key] ) { return; } this._obsMap[key].forEach(observer =>

Observer.update ();}

When a property changes, notify observers who subscribe to this property. Iterate through each observer and call the observer's update method. As we mentioned above, dirty inspection is done in this method.

The fifth part

Subject.prototype.setParent = function (parent, key) {this._parent = parent; this._key = key;}; Subject.prototype.notifyParent = function () {this._parent & & this._parent.notify (this._key);}

This section is used to deal with the problem of nested object. It's something like this: {user: {name: 'JJC'}}.

Part VI

Function hijack (target, key, cache) {const subject = target [Sybiol _ OBSERVABLE]; Object.defineProperty (target, key, {enumerable: true, get () {const observer = ObserverStack.top (); if (observer) {observer.subscribe (subject, key);} const subSubject = Subject.of (cache); if (Subject.is (subSubject)) {subSubject.setParent (subject, key) } return cache;}, set (value) {cache = value; subject.notify (key);});}

This section shows how to use Object.defineProperty for property hijacking. When the property is set, set (value) is called, the new value is set, and then the notify method of subject is called. There is no check here, and it will be called as long as the property is set, even if the new value of the property is the same as the old value. Notify will notify all observers.

The seventh part is hijacking array method.

Const ObservedMethods = {PUSH: 'push', POP:' pop', UNSHIFT: 'unshift', SHIFT:' shift', SPLICE: 'splice', REVERSE:' reverse'}; const OBSERVED_METHODS = Object.keys (ObservedMethods). Map (key = > ObservedMethods [key])

ObservedMethods defines the array functions that need to be hijacked. The uppercase in front is used to do key, and the lowercase in the back is the method that needs to be hijacked.

Function hijackArray (target) {OBSERVED_METHODS.forEach (key = > {const originalMethod = target [key]; defineProp (target, key, function () {const args = Array.prototype.slice.call (arguments); originalMethod.apply (this, args); let inserted; if (ObservedMethods.PUSH = = key | | ObservedMethods.UNSHIFT = = key) {inserted = args;} else if (ObservedMethods.SPLICE) {inserted = args.slice (2) } if (inserted & & inserted.length) {inserted.forEach (Subject.of);} const subject = target [Sybiol _ OBSERVABLE]; if (subject) {subject.notifyParent ();}});});}

Unlike objects, array hijacking cannot use Object.defineProperty.

We need to hijack 6 array methods. They are header addition, head deletion, tail addition, tail deletion, replacement / deletion, and array reversal.

The array hijacking is realized by rewriting the array method. But there is one thing to note here. Every element of the data has been observed, but when new elements are added to the array, they have not been observed. Therefore, the code also needs to determine that if the current method is push, unshift, splice, then the new element needs to be placed in the observer queue.

These are all the contents of the article "Javascript Framework case Analysis". Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report