- Data driven
- The core principles of responsiveness
- Publish subscribe mode and observer mode
Data driven
-
Data responsive, bidirectional binding, data driven
-
Data response
- The data model is just a normal Javascript object, and when we modify the data, the view is updated, avoiding tedious DOM manipulation and improving development efficiency
-
Two-way binding
- Data changes, view changes; As the view changes, so does the data
- We can use v-Models to create two-way data binding on form elements
-
Data-driven is one of the most unique features of VUE
- You just need to focus on the data itself, not how the data is rendered into the view, right
Core principles of data responsiveness
vue2
Object.defineproperty
Object.defineproperty (vm, 'MSG ', {enumerable: {}}) {}} let data = {MSG: 'hello'} let vm = {} object.defineProperty (vm,' MSG ', {enumerable: {}) {} true, configurable: true, get() { return data.msg } set(newValue) { if (newValue === data.msg) { return } data.msg = newValue document.querySelector('#app').textContent = data.msg } })Copy the code
let data = { msg: 'hello', count: 100} let vm = {} // data hijacking; Object. Keys (data). ForEach (key => {Object. DefineProperty (vm, 'MSG ', {enumerable: true, different: true, get() { return data[key] } set(newValue) { if (newValue === data[key]) { return } data[key] = newValue document.querySelector('#app').textContent = data[key] } }) })Copy the code
vue3
Proxy entire object
let data = { msg: 'hello', count: 100} let vm = new Proxy(vm, {enumerable: {}}) {} True,#### Vuejs Responsive principles - Data-driven - Responsive core principles - publish subscribe and observer mode ##### [](# data-driven) Data-driven - data responsive, bidirectional binding, data-driven - data responsive - The data model is just a common Javascript object. When we modify the data, the view will be updated to avoid tedious DOM operations and improve development efficiency. - Two-way binding - Data changes, view changes; As the view changes, so does the data - we can use the V-Model to create two-way data binding on the form elements - data-driven is one of the most unique features of VUE - you only need to focus on the data itself during development, You don't need to care about how the data is rendered to the view ##### [](# data responsiveness) ###### [](#vue2)vue2 object.definePropertyCopy the code
Object.defineproperty (vm, ‘MSG ‘, {enumerable: {}}) {}} let data = {MSG: ‘hello’} let vm = {} object.defineProperty (vm,’ MSG ‘, {enumerable: {}) {} true, configurable: true, get() { return data.msg } set(newValue) { if (newValue === data.msg) { return } data.msg = newValue
document.querySelector('#app').textContent = data.msg
Copy the code
}})
Copy the code
let data = { msg: ‘hello’, count: 100} let vm = {} // data hijacking; Object. Keys (data). ForEach (key => {Object. DefineProperty (vm, ‘MSG ‘, {enumerable: true, different: true, get() { return data[key] } set(newValue) { if (newValue === data[key]) { return } data[key] = newValue document.querySelector(‘#app’).textContent = data[key] } }) })
###### [](#vue3)vue3 proxies the entire objectCopy the code
let data = { msg: ‘hello’, count: Enumerable: {} Let vm = new Proxy(VM, {enumerable: true, different: 64x);} true, get(target, key) { return target[key] } set(target, key, newValue) { if (target[key] === newValue) { return } target[key] = newValue
document.querySelector('#app').textContent = data.msg
Copy the code
}})
##### [](# publish/subscribe and Observer) publish/subscribe and observer ##### [](# publish/subscribe) Publish/subscribe - Subscriber - Publisher - Signal center Publish a signal to the signal center when a task is completed, Other tasks can subscribe to this signal from the signal center to know when they can start executing. This is called a publish-subscribe pattern.Copy the code
// let vm = new VUe ()
// Subscribe message vm.on(‘dataChange’, ()=>console.log(‘dataChange’))vm.on(‘dataChange’, () = > {the console. The log (‘ dataChange ‘)}) vm) on (‘ dataChange ‘, () = > console. The log (‘ dataChange ‘)) vm. On (‘ dataChange ‘, () => {console.log(‘dataChange1’)}) $emit(‘dataChange’)
Copy the code
EventBus let eventHub = new Vue()
// componentA. Vue addTodo: function () {eventHub.$emit(‘add-todo’, {text: this.newTodoText }) this.newTodoText = ” }
// Componentb.vue subscription messages created() {eventHub.$on(‘add-todo’, this.addtodo)}
###### [](# Principle) principleCopy the code
Constructor () {// {‘click’: [‘fn1’, ‘fn2’], ‘change’: [fn] } this.subs = Object.create(null) }
$on(eventType, Handler) {this. Subs [eventType] = this. Subs/eventType | | [] this. Subs [eventType]. Push (handler)} / / trigger events $emit(eventType) { if (this.subs[eventType]) { this.subs[eventType].forEach(handler => { handler() }) } }Copy the code
}
Em.on (‘ em.on ‘, ()=>console.log(‘ em.on ‘))em.on(‘ em.on ‘, () = > {the console. The log (‘ click ‘)}) em. On (‘ click ‘, () = > console. The log (‘ click ‘)) em. Emit (‘ click ‘)
##### [](# observer mode) Observer mode - Observer (subscriber) Watcher-update (): What to do when an event occurs - target (publisher) - subs array: Store all observers -addSub (): Add observers -notify (): The Update () method that calls all observers has no event center when the time occursCopy the code
Constructor () {this.subs = []} addSub(sub) {if (sub && sub.update) {this.subs.push(sub)}} Notify () {this.subs.foreach (sub => {sub.update()})} class Watcher {update() {console.log(‘update’)} }
let dep = new Dep() let watcher = new Watcher() dep.addSub(watcher) dep.notify()
- The observer mode is scheduled by a specific target, for example, when an event is triggered, the Dep will call the observer method, so there is a dependency between the observer mode subscribers and publishers - publish/subscribe mode is called by the unified dispatch center, So publishers and subscribers do not need to know that each other exists #### [](#vue) Vue functions - responsible for receiving initialization parameters - inject properties from data into vue, Install getters and setters - Observer listens for changes to all properties in data - is responsible for calling compiler parsing instructions/interpolation templatesCopy the code
Difference expression
{{ msg }}
{{ count }}
v-text
v-model
js/vue.js
Class Vue {constructor (options) {/ / through the attribute data in a save option this. $options = options | | {} this. $data = options. The data | | {} this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : Options. el // convert data members to setters and getters this._proxyData(this.$data) // Observer(this.$data) // New Compiler(this)} _proxyData(data) {// Inject attributes from data into vue object.keys (data).foreach (key => { Object.defineProperty(this, key, { configurable: true, enumerable: true, get() { return data[key] }, set(newValue) { if (newValue === data[key]) { return } data[key] = newValue } }) }) } }Copy the code
js/observer.js
class Observer { constructor(data) { this.walk(data) } walk(data) { if (! data || typeof data ! == 'object') return Object.keys(data).forEach(key => { this.defineReactive(data, key, data[key]) }) } defineReactive(obj, key, Let dep = new dep () this.walk() object.defineProperty (obj, key, {64x: 64x) {const that = new dep () this.walk() object.defineProperty (obj, key, {64x: 64x) true, enumerable: true, get() { Dep.target && dep.addSub(Dep.target) return val }, Set (newValue) {if (newValue === val) {return} val = newValue that.walk(newValue) // Send notification dep.nodify()}})}}Copy the code
js/compiler.js
Class Compiler {constructor(vm) {this.el = vm.$el this.vm = vm this.compile(this.el)} Compile (el) {let childNodes = el.childNodes array. from(childNodes).foreach (node => {if) (this.isTextNode(node)) { this.compileText(node) } else if (this.isElementNode(node)) { this.compileElement(node) } if (node.childnodes && node.childnodes.length) {this.compile(node)}})} // Compile the element node, Log (array. from(node.attributes)) array. from(node.attributes).foreach (attr => {fetch element (node) {// console.log(array. from(node.attributes)) 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, attrName) } }) } update(node, key, atterName) { let updateFn = this[atterName + 'Updater'] updateFn && updateFn.call(this, node, this.vm[key], TextUpdater (node, value, key) {node. TextContent = value new Watcher(this.vm, key, (newValue) => { node.textContent = newValue }) } // v-model modelUpdater (node, value, key) { node.value = value new Watcher(this.vm, key, (newValue) => { node.value = newValue }) node.addEventListener('input', () => {this.vm[key] = node.value})} Matches any single character.+ matches once or more.+? End the match as soon as possible to compileText(node) {const reg = /{{(.+?) }}/ / () group let value = node.textContent if (reg.test(value)) {RegExp = RegExp.$1.trim() Node.textcontent = value.replace(reg, this.vm[key]) // Create watcher object new Watcher(this.vm, key, (newValue) => {node.textContent = newValue})}} isDirective(attrName) {return Attrname.startswith ('v-')} isTextNode(node) {return node.nodeType === 3} // Check whether the node is an element node isElementNode(node) { return node.nodeType === 1 } }Copy the code
js/dep.js
Constructor () {this.subs = []} addSub(sub) {if (sub && sub.update) {this.subs.push(sub)}} // Nodify () {this.subs.foreach (sub => {sub.update()})}}Copy the code
js/watcher.js
class Watcher { constructor(vm, key, Cb) {this.vm = vm this.key = key // Update view this.cb = cb // Record watcher to Dep class static attribute target dep. target = this this.oldValue = Vm [key] dep.target = null} update() {let newValue = this.vm[this.key] if (this.oldValue === newValue) { return } this.cb(newValue) } }Copy the code
configurable: true, get(target, key) { return target[key] } set(target, key, newValue) { if (target[key] === newValue) { return } target[key] = newValue
document.querySelector('#app').textContent = data.msg
Copy the code
}})
##### [](# publish/subscribe and Observer) publish/subscribe and observer ##### [](# publish/subscribe) Publish/subscribe - Subscriber - Publisher - Signal center Publish a signal to the signal center when a task is completed, Other tasks can subscribe to this signal from the signal center to know when they can start executing. This is called a publish-subscribe pattern.Copy the code
// let vm = new VUe ()
// Subscribe message vm.on(‘dataChange’, ()=>console.log(‘dataChange’))vm.on(‘dataChange’, () = > {the console. The log (‘ dataChange ‘)}) vm) on (‘ dataChange ‘, () = > console. The log (‘ dataChange ‘)) vm. On (‘ dataChange ‘, () => {console.log(‘dataChange1’)}) $emit(‘dataChange’)
Copy the code
EventBus let eventHub = new Vue()
// componentA. Vue addTodo: function () {eventHub.$emit(‘add-todo’, {text: this.newTodoText }) this.newTodoText = ” }
// Componentb.vue subscription messages created() {eventHub.$on(‘add-todo’, this.addtodo)}
###### [](# Principle) principleCopy the code
Constructor () {// {‘click’: [‘fn1’, ‘fn2’], ‘change’: [fn] } this.subs = Object.create(null) }
$on(eventType, Handler) {this. Subs [eventType] = this. Subs/eventType | | [] this. Subs [eventType]. Push (handler)} / / trigger events $emit(eventType) { if (this.subs[eventType]) { this.subs[eventType].forEach(handler => { handler() }) } }Copy the code
}
Em.on (‘ em.on ‘, ()=>console.log(‘ em.on ‘))em.on(‘ em.on ‘, () = > {the console. The log (‘ click ‘)}) em. On (‘ click ‘, () = > console. The log (‘ click ‘)) em. Emit (‘ click ‘)
##### [](# observer mode) Observer mode - Observer (subscriber) Watcher-update (): What to do when an event occurs - target (publisher) - subs array: Store all observers -addSub (): Add observers -notify (): The Update () method that calls all observers has no event center when the time occursCopy the code
Constructor () {this.subs = []} addSub(sub) {if (sub && sub.update) {this.subs.push(sub)}} Notify () {this.subs.foreach (sub => {sub.update()})} class Watcher {update() {console.log(‘update’)} }
let dep = new Dep() let watcher = new Watcher() dep.addSub(watcher) dep.notify()
- The observer mode is scheduled by a specific target, for example, when an event is triggered, the Dep will call the observer method, so there is a dependency between the observer mode subscribers and publishers - publish/subscribe mode is called by the unified dispatch center, So publishers and subscribers do not need to know that each other exists #### [](#vue) Vue functions - responsible for receiving initialization parameters - inject properties from data into vue, Install getters and setters - Observer listens for changes to all properties in data - is responsible for calling compiler parsing instructions/interpolation templatesCopy the code
Difference expression
{{ msg }}
{{ count }}
v-text
v-model
js/vue.js
Class Vue {constructor (options) {/ / through the attribute data in a save option this. $options = options | | {} this. $data = options. The data | | {} this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : Options. el // convert data members to setters and getters this._proxyData(this.$data) // Observer(this.$data) // New Compiler(this)} _proxyData(data) {// Inject attributes from data into vue object.keys (data).foreach (key => { Object.defineProperty(this, key, { configurable: true, enumerable: true, get() { return data[key] }, set(newValue) { if (newValue === data[key]) { return } data[key] = newValue } }) }) } }Copy the code
js/observer.js
class Observer { constructor(data) { this.walk(data) } walk(data) { if (! data || typeof data ! == 'object') return Object.keys(data).forEach(key => { this.defineReactive(data, key, data[key]) }) } defineReactive(obj, key, Let dep = new dep () this.walk() object.defineProperty (obj, key, {64x: 64x) {const that = new dep () this.walk() object.defineProperty (obj, key, {64x: 64x) true, enumerable: true, get() { Dep.target && dep.addSub(Dep.target) return val }, Set (newValue) {if (newValue === val) {return} val = newValue that.walk(newValue) // Send notification dep.nodify()}})}}Copy the code
js/compiler.js
Class Compiler {constructor(vm) {this.el = vm.$el this.vm = vm this.compile(this.el)} Compile (el) {let childNodes = el.childNodes array. from(childNodes).foreach (node => {if) (this.isTextNode(node)) { this.compileText(node) } else if (this.isElementNode(node)) { this.compileElement(node) } if (node.childnodes && node.childnodes.length) {this.compile(node)}})} // Compile the element node, Log (array. from(node.attributes)) array. from(node.attributes).foreach (attr => {fetch element (node) {// console.log(array. from(node.attributes)) 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, attrName) } }) } update(node, key, atterName) { let updateFn = this[atterName + 'Updater'] updateFn && updateFn.call(this, node, this.vm[key], TextUpdater (node, value, key) {node. TextContent = value new Watcher(this.vm, key, (newValue) => { node.textContent = newValue }) } // v-model modelUpdater (node, value, key) { node.value = value new Watcher(this.vm, key, (newValue) => { node.value = newValue }) node.addEventListener('input', () => {this.vm[key] = node.value})} Matches any single character.+ matches once or more.+? End the match as soon as possible to compileText(node) {const reg = /{{(.+?) }}/ / () group let value = node.textContent if (reg.test(value)) {RegExp = RegExp.$1.trim() Node.textcontent = value.replace(reg, this.vm[key]) // Create watcher object new Watcher(this.vm, key, (newValue) => {node.textContent = newValue})}} isDirective(attrName) {return Attrname.startswith ('v-')} isTextNode(node) {return node.nodeType === 3} // Check whether the node is an element node isElementNode(node) { return node.nodeType === 1 } }Copy the code
js/dep.js
Constructor () {this.subs = []} addSub(sub) {if (sub && sub.update) {this.subs.push(sub)}} // Nodify () {this.subs.foreach (sub => {sub.update()})}}Copy the code
js/watcher.js
class Watcher { constructor(vm, key, Cb) {this.vm = vm this.key = key // Update view this.cb = cb // Record watcher to Dep class static attribute target dep. target = this this.oldValue = Vm [key] dep.target = null} update() {let newValue = this.vm[this.key] if (this.oldValue === newValue) { return } this.cb(newValue) } }Copy the code