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
- New Vue() performs initialization by hand, performing reactive processing on data, which occurs in the Observer
- 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
- Define an update function and Watcher, which Watcher will call in the future when the corresponding data changes
- Because a key of Data may appear multiple times in a view, each key requires a steward Dep to manage multiple Watcher
- 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