What is the MVVM
MVVM is an architectural pattern consisting of three parts, where M represents Model (data layer), V represents View (View layer), and VM represents ViewModel (bridge between View and Model). As data changes in the data layer, so does the view layer.
For our front end, we don’t have to worry about how to add, delete, or modify the DOM. This is all done by our framework. The ViewModel first listens to the data, and when the data changes, the ViewModel notifies the view layer that it needs to update.
MVVM implementation ideas
For the above process it looks like this:
- The data layer is first hijacked, listening for each property, which has a container Dep that manages the observer.
- When a view-layer node references a property, the node creates a Watcher and adds the Watcher to the property’s Dep, thus establishing a relationship between the two.
- When the attributes of the data layer change, the corresponding Dep notifies the node (observer) of the update.
This implements a completed MVVM process.
The data was hijacked
Use JS object.defineProperty to listen for each attribute of the data.
Function isUndefined(data) {return data === undefined; Function isNull(data) {return data === Null; Function isBaseType(data) {const type = typeof data; const baseType = ["string", "number", "boolean"]; return ( baseType.findIndex((v) => v === type) > -1 || isUndefined(data) || isNull(data) ); Function isArray(data) {return data instanceof Array; } function observer(data) { if (isBaseType(data) || isArray(data)) return data; Object.keys(data).forEach((key) => { defineReactive(data, key, data[key]); }); } function defineReactive(data, key, value) { observer(value); const dep = new Dep(); Object.defineproperty (data, key, {get() {console.log(" I'm being used "); Dep.target && dep.addDep(Dep.target); // Collect observer return value; }, set(newValue) { if (newValue === value) return null; value = newValue; dep.notify(); // Notify the observer to update console.log(" data has changed "); }}); }Copy the code
Who manages the observers
Constructor () {// This. Dep = []; // Dep: constructor() {// This. } addDep(dep) { this.deps.push(dep); } notify() { this.deps.forEach((dep) => dep.update()); }}Copy the code
Suppose you now have a node that references an attribute
An HTML is made up of many nodes. I now assume that there is a node that references the name attribute of data
class Node { constructor(key) { this.value = data[key]; new Watcher(data, key, (value) => { this.update(value); }); } update(value) {console.log("data is updated, I want to update "); this.value = value; } } // Watcher class Watcher { constructor(data, key, cb) { this.data = data; this.key = key; this.cb = cb; // Assign the current watcher instance to the Dep static attribute target dep. target = this; this.vm[this.key]; // Trigger getter, add dependency dep. target = null; } update() {// console.log(" Properties updated "); this.cb.call(this.data, this.data[this.key]); }}Copy the code
In the above code, the Node constructor references data’s name property, which creates an observer (watcher) and adds it to the Observer management container (DEP).
When the properties of the data layer change
When data changes, dep.notify() notifies the used observer of the update.
The complete code is as follows:
Const data = {name: "ultraman ", info: {sex: "man",},}; Function isUndefined(data) {return data === undefined; Function isNull(data) {return data === Null; Function isBaseType(data) {const type = typeof data; const baseType = ["string", "number", "boolean"]; return ( baseType.findIndex((v) => v === type) > -1 || isUndefined(data) || isNull(data) ); Function isArray(data) {return data instanceof Array; } function observer(data) { if (isBaseType(data) || isArray(data)) return data; Object.keys(data).forEach((key) => { defineReactive(data, key, data[key]); }); } function defineReactive(data, key, value) { observer(value); const dep = new Dep(); Object.defineproperty (data, key, {get() {console.log(" I'm being used "); Dep.target && dep.addDep(Dep.target); return value; }, set(newValue) { if (newValue === value) return null; value = newValue; dep.notify(); Console. log(" Data changed "); }}); } class Node { constructor(key) { this.value = data[key]; new Watcher(data, key, (value) => { this.update(value); }); } update(value) {console.log("data is updated, I want to update "); this.value = value; } } // Watcher class Watcher { constructor(data, key, cb) { this.data = data; this.key = key; this.cb = cb; // Assign the current watcher instance to the Dep static attribute target dep. target = this; this.data[this.key]; // Trigger getter, add dependency dep. target = null; } update() {// console.log(" Properties updated "); this.cb.call(this.data, this.data[this.key]); }} // Dep: constructor {constructor() {// This. Dep = []; } addDep(dep) { this.deps.push(dep); } notify() { this.deps.forEach((dep) => dep.update()); } } observer(data); const node = new Node("name"); Console. log(node, "before new "); Data. name = "dija "; data.name =" dija "; Console. log(node, "after update ");Copy the code