The MVVM framework

The MVVM framework has three elements: data responsiveness, template engine and its rendering

Data responsive: Listens for data changes and updates in the view

  • Object.defineProperty()
  • Proxy()

Template engine: Provides template syntax for describing views

  • The interpolation {{}}
  • The instructions v-bind, V-ON, V-model, V-for, V-if

Render: How do I convert templates to HTML

  • Template = > vdom = > dom

Data responsivity principle

Data responsiveness means that data changes can be reflected in the view. Vue2 uses the object.defineProperty () method to do this

defineProperty.js

// object.defineProperty () // Intercept: intercepts a key of a pair, note: Function defineReactive(obj, key, val) {// If val is an object, Observe (val) object.defineProperty (obj, key, {get() {return val}, set(newVal){if(val! Observe (newVal) val = newVal; Update ()}})} function upate() {update view dom.innerhtml = XXX} Function observe(obj) {if(typeof obj! == 'object' || obj === null) return obj Object.keys(obj).forEach(key => { defineReative(obj, key, Function set(obj, key, val) {defineReactive(obj, key, val)}Copy the code

The principle of analysis

  1. New Vue() performs initialization by hand, performing reactive processing on data, which occurs in the Observer
  2. At the same time, the template is compiled, the dynamically bound data is found, the view is obtained from the data and initialized, this process happens in Compile
  3. Define an update function and Watcher, which Watcher will call in the future when the corresponding data changes
  4. Because a key of Data may appear multiple times in a view, each key requires a steward Dep to manage multiple Watcher
  5. Once the data in the data changes in the future, the corresponding Dep will be found first and all Watcher will be informed to perform the update function

Involved Types

  • Vue: frame constructor
  • Observer: Perform data reactivity (distinguish whether data is an object or an array)
  • Compile: Compile templates, initialize views, collect dependencies (update functions, create by Watcher)
  • Watcher: Execute update function (update DOM)
  • Dep: Manage multiple Watcher, batch update

vue.js

//1. Implement vUE constructor //2. Make data responsive // array responsive //1 Const orginalProto = array. prototype Const arrayProto = object. create(orginalProto) ['push', 'pop', 'shift', ForEach (method => {arrayProto[method] = function() {// orginalProto[method]. Apply (this, arrayProto[method]). Console. log(' array execution '+ method +' action '); }}) // object.defineProperty () // Intercepting: intercepting a key of a pair, note: Function defineReactive(obj, key, val) {// If val is an object, Observe (val) // create const dep = new dep () object.defineProperty (obj, key, Target && dep.adddep (dep.target) return val}, set(newVal){if(val! Observe (newVal) val = newVal () {observe(newVal) val = newVal () Voild dep.notify()}})} function upate() {update view dom.innerhtml = XXX} Function observe(obj) {if(typeof obj! = = 'object' | | obj = = = null) return obj judgment to obj type if (Array. IsArray (obj)) {/ / cover prototype, // Const keys = object. keys(obj) for(let I = 0; i<obj.length; I ++) {observe(obj[I])} else {object.key (obj).foreach (key => {defineReactive(obj, key, obj[key])})} // new Observer(obj)} // proxy function: Key function proxy(vm, key) {objet.keys (vm[key]). ForEach (k => {object.devineProperty (vm, k,); {the get () {return vm [key] [k]}, the set (v) {vm [key] [k] = v}})})} / / according to the different type of incoming, Class Observer {constructor(value) {this.value = value // Check the type of the object this.walk(value)} walk(obj) { Key => {defineReactive(obj, key, obj[key])})} val) { defineReactive(obj, key, val) } class Vue { constructor(options) { //1. $options = options this.$data = options.data // 2. Observer (this.$data) // 3. Proxy(this, '$data')}} // class Compile {// el- host elements, Vm - > Vue instance constructor (el, If (this.$el) {this.compile(this.$el)}}. If (this.$el) {this.compile(this.$el)}} Compile (el) {// el is the host element // iterate over it, ForEach (node => {if(node.nodeType === 1) {// compile this.pileElement (node) // recurse This.compile (node)} else if(this.isinter (node)) { {{XXX}} this.piletext (node)}}} isInter(node) {return node.nodeType === 3&& / \ {\ {\ (. *) \} \} /. The test (node. TextContent)} / / compile text compileText (node) {/ / node. TextContent = this. $[RegExp. $1] vm This.update (node, RegExp.$1, 'text')} // Fetch attributes and loop const node.attributes Array.from(nodeAttrs).foreach (attr => {// Fetch attributes and loop const node.attributes Array.from(nodeAttrs). / / instructions:  v-xxx = y const attrName = attr.name //v-xxxx const exp = attr.value // xxx if(this.isDirective(attrName)){ consst dir= Attrname. substring(2) // XXX // this[dir] && this[dir](node, If (this.isevent (attrName)) {//@click="onClick" const dir = attrname.substring (1) // click // Sleep listener this.eventHandler(node, exp, IsDirective (attr) {return attr.indexof ('v-') === 0} isEvent(dir) {return dir.indexof ('@') === 0 } eventHandler(node, exp, dir) { const fn = this.$vm.$options.methods && this.$vm.$options.methods[exp] node.addEventListener(dir, Fn. Bind (this.$vm))} text(node, exp) {this.update(node, exp, 'text')} val) { node.textContent = this.$vm[exp] } html(node, exp) { this.update(node, exp, 'html') } htmlUpdater(node, Exp) {node.innerhtml = this.$vm[exp]} //v-model='xx' model(node, exp) { Node.addeventlistener ('input', e => {this.$vm[exp] = e.target.value})} modelUpdater(node, e => {this.$vm[exp] = e.target.value})} Value) {// Form element assignment node.value = value} // Extract update, initialize the update function to create update(node, exp, {const fn = this[dir+'Updater] // Initialize fn && fn(node, this.$vm[exp]) // update new Watcher(this.$vm, exp, function(val) { fn && fn(node, this.$vm[exp]) }) } } // Watcher: Small secretary, with view dependency 1:  1 class Watch { constructor(vm, key, UpdaterFn) {this.vm = vm this.key = key this.updaterFn = updaterFn // rely on collection to trigger dep. target = this // define global variables Target = null} update() {this.updaterfn. Call (this.vm, this.vm[this.key])}} One to one, managing multiple secretaries, Class Dep {constructor() {this.deps = []} addDep(watcher) {this.deps. Push (watcher)} notify() { this.deps.forEach(watcher => watcher.update()) } }Copy the code