Vue overall structure

  • Vue: Inject data members into Vue instances and convert data members into getters and setters
  • Observer: Hijacks all attributes of the object and notifies the Dep of any changes to the latest values
  • Compiler: Parses the instructions/interpolations in each element and replaces them with the corresponding data
  • Dep: publisher. Collect observers and notify all observers when data changes
  • Watcher: An observer. Data changes update the view

Vue (initialization)

  • function
    • Responsible for receiving initialization parameters (options)
    • Is responsible for injecting properties from Data into Vue instances and converting them into getters/setters
    • Responsible for calling the Observer to listen for changes to all properties in data
    • Responsible for calling compiler parsing instructions/interpolation expressions
  • structure

  • code
class Vue { constructor(options) { // 1. Through property preservation option data enclosing $options = options const {el, data} = options this. $data = data | | {} / / if the selector, This.$el = typeof el === 'string'? document.querySelector(el) : el // 2. Getters and setters for data. this._proxydata (data) // 3. New Observer(data) // 4. Calling the Compiler object, New Compiler(this)} /** * proxy data, convert properties in data to getters, Setter * @param {*} data */ _proxyData(data) {// Traverses all properties of data object.keys (data).foreach (key => {// inject data's properties into vue instance DefineProperty (this, key, {enumerable: true, // Enumerable: different: Get () {return data[key]}, set(newValue) { if (data[key] === newValue) return data[key] = newValue } }) }) } }Copy the code

Observer (Observer)

  • function
    • Convert each property in the Data option to reactive data
    • If a property in data is also an object, that property should also be converted into responsive data
    • Notification is sent when data changes
  • structure

  • code
// Class Observer {constructor(data) {this.walk(data)}} @param {*} data */ /  1. Check whether data is an object if (! data || typeof data ! Keys (data). ForEach (key => {this.definereactive (data, key, key); Data [key])})} /** * @param {*} obj object * @param {*} key * @param {*} val value */ defineReactive(obj, key, Const dep = new dep () const dep = new dep (); // If val is an object, convert the attributes inside the val into responsive data, DefineProperty (obj, key, {enumerable: true, different: 64x) {this.walk(val) Object. Dep.target && dep.addSub(dep.target) return val // obj[key] So the above method needs to pass val}, Set (newVal) {if (val === newVal) return val = newVal that.walk(newVal) // Send notification dep.notify()}})}}Copy the code

Compiler (parser)

  • function
    • Responsible for compiling templates, parsing instructions/interpolation expressions
    • Responsible for the first rendering of the page
    • Re-render the view when the data changes
  • structure

  • code
/ / class Compiler {constructor (vm) {this.el = vm.$el this.vm = vm this.compile(this.el)} * @param {*} el */ compile (el) {const childNodes = el.childnodes // pseudo-array Array.from(childNodes).foreach (node => {if (this.istextNode (node)) {// Handle text node this.piletext (node)} else if This.iselementnode (node)) {this.iselementNode (node)) {this.iselementNode (node)) {this.iselementNode (node)} To compile if (node.childnodes && node.childnodes.length) {this.compile(node)}})} /** * compile element nodes, * @param {*} node element node */ comileElement(node) {const attributes = node.attributes // pseudo-array // traverses all attribute nodes Array.from(node.attributes).foreach (attr => {let attrName = attr.name if (this.isdirective (attrName)) {// V-text => text attrName = attrname. substr(2) let key = attr.value this. Update (node, key, @param {*} node node * @param {*} key attribute * @param {*} attrName attribute name * @param {*} key attribute name * @param {*} attrName attribute name * @param {*} key attribute name * @param {*} attrName attribute name * @param {*} key attribute name * @param {*} attrName attribute name */ update(node, key, AttrName) {let updateFn = this[' ${attrName}Updater '] // call(this, node, this.vm[key], Key) // call, /** * Handle v-text instructions * @param {*} node node * @param {*} value text content * @param {*} key create Watcher */ TextUpdater (node, value, key) {node.textContent = value // New Watcher(this.vm, key, (newValue) => {node.textContent = newValue})} /** * Handle the V-model instruction * @param {*} node * @param {*} value * @param {*} key */ modelUpdater(node, value, key) {node.value = value // new Watcher(this.vm, key, (newVal) => {node.value = newVal}) // Two-way binding node.addeventListener ('input', () = > {this. [key] = node vm. Value})} / * * * compiled text node, @param {*} node element node */ compileText (node) {let reg = /\{\{(.+?)\}\}/ / re const Content = node.textContent If (reg.test(content)) {let key = RegExp.$1.trim() node.textContent = content.replace(reg, This.vm [key]) // first render // create the watcher object, when the data changes to update the view, the corresponding value of key changes, The callback function new Watcher(this.vm, key, (newValue) => {node.textContent = newValue})}} /** * Determine if the element attribute is a directive * @param {*} attrName attribute name */ isDirective (attrName) {return attrname. startsWith('v-')} /** * Check whether the node is a text node * @param {*} node node */ isTextNode (node) {return Node.nodetype === 3} @param {*} node node */ isElementNode (node) {return node.nodeType === 1}}Copy the code

Dep (Dependency Publisher)

  • function
    • Collect dependencies, add watcher
    • Notify all observers
  • structure

  • code
/** * publisher * For each monitored attribute, Watchs = []} /** * Adds the getter for the property being monitored (the observer corresponding to the property being monitored) * @param {*} watch watch */ addSub(watch) {if (watch &&watch.update) {// Watch, Must have update method this.watchs.push(watch)}} /** * for all observers, */ notify() {this.watchs.foreach (watch => {watch.update()})}Copy the code

Watcher

  • function
    • Data changes are trigger dependencies, and DEP notifies all Watcher instances to update the view
    • When instantiating itself, you add yourself to the DEP object
  • structure

  • code
/** * observer, which corresponds to the directive attribute in the node of the page * for example: V-text: XXX, {{XXX}} */ class Watcher {/** * @param {*} VM vue instance * @param {*} key Observe the specific attributes * @param {*} cb The corresponding callback function (method to update the DOM) */ constructor(vm, key, Cb) {this.vm = vm // Data attribute name this.key = key // the callback is responsible for updating the view this.cb = cb // Record the watcher object to the Dep static attribute target, Target = this // record the historical value, vm[key] triggers the getter for the Watcher property, This. oldValue = vm[key] dep. target = null // clear, Update () {let newValue = this.vm[this.key] if (this.oldValue === newValue) return This.cb (newValue) // Update view}}Copy the code

test

<! DOCTYPE html> <html lang="cn"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta HTTP-equiv =" x-UA-compatible "content=" IE =edge"> <title>Vue lite </title> </head> <body> <div Id = "app" > < h1 > difference expression < / h1 > < h3 > {{MSG}} < / h3 > < h3 > {{count}} < / h3 > < h1 > v - text < / h1 > < div v - text = "MSG" > < / div > <h1>v-model</h1> <input type="text" v-model="msg"> <input type="text" v-model="count"> </div> <script src="./js/dep.js"></script> <script src="./js/watcher.js"></script> <script src="./js/compiler.js"></script> <script src="./js/observer.js"></script> <script src="./js/vue.js"></script> <script> let vm = new Vue({ el: '#app', data: { msg: 'Hello Vue', count: 100, person: { name: 'zs' } } }) console.log(vm.msg) // vm.msg = { test: 'Hello' } vm.test = 'abc' </script> </body> </html>Copy the code

conclusion

  • Vue
    • Log the incoming options and set data/data/data/el
    • Inject members of Data into Vue instances
    • Responsible for calling the Observer for responsive processing of data (data hijacking)
    • Responsible for calling Compiler to compile instructions/interpolation expressions, etc
  • Observer
    • The data was hijacked
      • Is responsible for converting members in data to getters/setters
      • Is responsible for converting layers of properties into getters/setters
      • Is responsible for converting members in data to getters/setters
    • Add the Dep and Watcher dependencies
    • Send notifications of data changes
  • Compiler
    • Responsible for compiling templates, parsing instructions/interpolation expressions
    • Responsible for the first rendering of the page
    • Re-render when the data changes
  • Dep
    • Collect dependencies, add subscribers (Watcher)
    • Notify all subscribers
  • Watcher
    • Add yourself to the DEP object when instantiating itself
    • When data changes to the DEP, notify all watcher instances of the updater to update the view