Mvvm-simple bidirectional binding is simple

bestvist 2018-04-17

MVVM mode frees the DOM shackles






MVVM principle analysis

JavaScript manipulating HTML in the browser goes through several different stages

The first stage directly manipulates DOM elements with native apis provided by the browser

var dom = document.getElementById('id');
dom.innerHTML = 'hello mvvm';
Copy the code

In the second stage, jQuery solves the complexity of native apis and compatibility between browsers, providing a simpler and more convenient API

$('#id').text('hello mvvm')
Copy the code

The third stage MVC pattern enables the front end to work with the back end to modify the page content rendered by the server

As products attach more importance to user experience, interactive experience becomes more and more important. JQuery alone is far from enough. MVVM Model solves the pain point of frequent operation, and model-view-ViewModel mode transfers data and View synchronization to ViewModel

JQuery Modify node content:

<p>name: <span id="name">vist</span>! </p> <p>age: <span id="age">25</span>.</p> var name = 'bestvist'; var age = 26; $('#name').text(name); $('#age').text(age);Copy the code

In MVVM mode, only the data structure needs to be concerned:

Var me = {name: 'vist', age: 25} me.age = 26;Copy the code

The MVVM implementation

MVVM implements data binding in several ways:

  • Publish and subscribe
  • Dirty value check
  • The data was hijacked

The more popular VUE adopts the data hijacking and published-subscribe mode. By hijacking the get and set methods of each attribute in Object.defineProperty() provided by ES5, the message is triggered to the subscriber when the data is updated, and the data binding function is realized.

Object.defineproperty (obj, prop, Descriptor) methods directly define a new property on an Object, or modify an existing property, and return the Object. The method accepts three parameters:

  • Obj The object that defines attributes.
  • Prop Specifies the name of the property to be defined or modified.
  • Descriptor Descriptor of a property to be defined or modified.

While properties are typically created or modified by assigning values directly to Object properties, using Object.defineProperty allows you to modify some additional default configurations of Object properties. Such as:

const obj = {name: 'Tom'}; Object.defineProperty(obj, 'name', { get: function(val) { return 'Jerry'; } }) console.log(obj.name); // Output: JerryCopy the code

Please stamp here

The main process of MVVM implementation:

  • Data proxy, which directly returns properties in the corresponding data when accessing properties on the instance
  • Data listening, which listens for properties on the instance and notifies subscribers of updates if data changes
  • Instruction parsing, parsing each element node, replacing data and binding update functions
  • Link data listening and instruction resolution to ensure that each data update, instruction resolution can obtain and update the view

Instantiate class:

new MVVM({
    el: '#app',
    data() {
        return {
            message: 'hello mvvm'
        }
    }
})
Copy the code

Data broker:

class MVVM { constructor(options) { this.$options = options || {}; let data = this._data = this.$options.data(); // Data agent vm. XXX => vm._data.xxx object.keys (data).foreach (key => {this._proxyData(key); }); // observe(data, this); // this.$compile = new Compile(options.el || document.body, this); } _proxyData(key) { Object.defineProperty(this, key, { configurable: true, enumerable: true, get: () => { return this._data[key]; }, set: newVal => { this._data[key] = newVal; }}); }}Copy the code

Data listening, hijacking instance property update

class Observer { constructor(data) { this.data = data; Object.keys(this.data).forEach(key => { this.defineReactive(key, this.data[key]); DefineReactive (key, val) {let dep = new dep (); Object.defineProperty(this.data, key, { enumerable: true, configurable: false, get: () => { return val; }, set: newVal => { if (val === newVal) { return; } val = newVal; // Assign the object to hijacking observe(val); . }} function observe(val) {if (! val || typeof val ! == 'object') { return; } return new Observer(val); }Copy the code

The instruction parses part of the code

class Compile { constructor(el, vm) { this.$vm = vm; this.$el = this.isElementNode(el) ? el : document.querySelector(el); if (this.$el) { this.$fragment = this.node2Fragment(this.$el); this.init(); this.$el.appendChild(this.$fragment); } } init() { this.compileElement(this.$fragment); } node2Fragment(el) { let fragment = document.createDocumentFragment(), child; // copy the original node to fragment while (child = el.firstChild) {// appendChild moves the element up from dom to fragment fragment.appendChild(child); } return fragment; } compileElement(el) { let childNodes = el.childNodes; [].slice.call(childNodes).forEach(node => { let text = node.textContent; let reg = /\{\{(.*)\}\}/; if (this.isElementNode(node)) { this.compile(node); } else if (this.isTextNode(node) && reg.test(text)) { this.compileText(node, RegExp.$1); } if (node.childNodes && node.childNodes.length) { this.compileElement(node); }}}})Copy the code

Among them

While (child = el.firstChild) {// appendChild moves the element up from dom to fragment fragment.appendChild(child); }Copy the code

AppendChild changes the original DOM structure by gradually moving DOM element nodes into fragments.

The complete code
Vue source

conclusion

The data-flow-oriented MVVM mode greatly simplifies front-end operations on DOM, speeds up front-end development, and improves user experience.

Reference:

Analysis of Vue principle & Realization of bidirectional binding MVVM MVVM Liao Xuefeng