One: The first thing to know
- Data driven
- The core principles of responsiveness
- Publish subscribe mode and observer mode
Two: data driven
- Data responsiveness: The data model is just a normal javaScript object, and when we modify the data, the view is updated to avoid tedious DOM manipulation and improve development efficiency.
- Bidirectional binding: data changes, view changes; The view changes and the data changes with it. 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, where you only need to focus on how the data is rendered into the view.
Three: data responsive core principle Vue2
- Object.defineProperty
-
A single attribute
Let data = {// Vue data option MSG = “Hello Word”} // Vue instance let vm = {}
Object.defineproperty (vm,” MSG “,{// Enumerable: // Deletes the specified information using delete and defineProperty. Get (){console.log(‘get:’,data.msg) return data.msg}, // Set (newValue){console.log(‘set:’newValue) if(newValue === data.msg){return} data.mag = newValue doucment.querySelector(“.app”).textContent = data.msg } })
Vm. MSG = ‘ha ha ha’ console.log(vm. MSG)
-
Multiple attributes
Let data = {Vue data MSG :’Hello Word’, count: 5}
// let vm = {} proxyData(data) function proxyData(data){object.keys (data).foreach (key => { Object. DefineProperty (vm, key, {// enumerable: // Deletes the specified information using delete and defineProperty. true, get(){ console.log(‘get:’,key,data[key]) return data[key] }, Set (newValue){console.log(‘set:’,key,newValue) if(newValue === data[key]){return} data[key] = newValue // Data change, update Dom value document.querySelector(‘#app’).textContent = data[key] }, }) }) }
vm.msg = ‘Hello World’ console.log(vm.msg)
Three: Data responsive core principle Vue3
-
Proxy
-
Listen directly on objects, not properties (so no traversal is required to process multiple properties)
-
New in ES6, not supported by IE, new can be optimized by browser
Let data = {MSG: ‘Hello Word’, count: 0}
// Get (targer, key){console.log(“get”, key){// Get (targer, key){console.log(“get”, key); Target [key]) rerurn target[key]}, // Set (target, key, newValue){console.log(“set”, key, newValue) if(target[key] === newValue){ return } target[key] = newValue document.querySelector(‘#app’).textContent = target[key] }, })
vm.msg = “Hello” console.log(vm.msg)
Four: publish and subscribe mode
-
Publish subscribe model (subscriber, publisher, signal center)
-
We assume that there is a ‘signal center’ where a task ‘publishes’ a signal to the signal center when it completes, and other tasks’ subscribe’ to the signal center to know when they can start executing. This is called the “publish subscriber model.”
-
Vue User-defined event
{click:[fn1,fn2],change:[fn1,fn2]}
// The first parameter is an event, 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(“dataChange2”) })
$emit(“dataChange”)
-
Sibling communication
// let eventHub = new Vue()
$emit(‘add-toda’,{text: this.newtodatext}) $emit(‘add-toda’,{text: this.newtodatext}) this.newTodaText = ” }
$on(‘add-toda’, this.addToda)} $on(‘add-toda’,this.addtoda)} $on(‘add-toda’,this.addtoda)} $on(‘add-toda’,this.addtoda)}
Five: simulate publish and subscribe model
< script SRC = "https://cdn.jsdelivr.net/npm/vue" > < / script > / class/event trigger EventEmitter {/ / record all events and the corresponding handler constructor () { //{"click":[fn1,fn2]} this.subs = {}} The second parameter is the event function $on (eventType, hander) {this. Sub [eventType] = this. Subs/eventType | | [] this. Subs [eventType]. Push (hander)} $emit(eventType){if(this.subs[eventType]){this.subs[eventType].foreach ((hander) => {$emit(eventType){if(this.subs[eventType]){this.subs[eventType]. } / / test let en = new EventEmitter () / / registered event em. $on (" click ", () = > {the console. The log (" click1 ")}) em. $on (" click ", () = > { Console. log('click2')}) // Triggers the event em.$emit('click') browser outputs click1 click2Copy the code
Six: Observer mode
-
Observer (Subscriber)-Watcher
-
Update (): Exactly what to do when an event occurs
-
Target (publisher) -dep
-
Subs array: Stores all observers
-
AddSub () : Adds an observer
-
Notify: When an event is sent, the update() method of all observers is called
-
No event center (unlike publishing subscribers)
-
Simulated observer model
Class Dep {constructor(){this.subs = []} // Add subscriber method addSub(sub){// Determine if the object exists and has update method if(sub &&) Sub.update){this.subs.push(sub)}} // Notify all subscribers of the occurrence of the event, Update (){this.subs.foreach ((sub) => {sub.update()})}}
Update (){console.log(‘update’)}} class Watcher {// publisher – target // subscriber – observer
let dep = new Dep() let watcher = new Watcher() dep.addSub(watcher) dep.notify()
Seven: the difference between publishing subscribers and observers
- The observer mode is scheduled by a specific target, such as an event, and the Dep calls the observer method, so there are dependencies between the observer mode subscribers
- The publish/subscribe model is invoked by the unified center, so publishers and subscribers do not need to know of each other’s existence
Eight: Simulation Vue response type principle – analysis
- Vue- Injects data members into Vue instances and converts data members into getters/setters
- Observer – Listens for all attributes of a data object, gets the latest values and informs Dep of any changes
- Compiler- Parses instructions/interpolations in each element and replaces them with the corresponding data
- The Dep(publisher) adds an observer (wathcer) to notify the observer when data changes
- Watcher – Updates the view with data changes
Eight: the Vue class
-
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 to solve instructions/interpolation expressions
class Vue { constructor(options){ //1. Through property preservation option data (the following attributes are used to record data from the options) / / if not by value is an empty object enclosing the options = options ∣ ∣ enclosing the options = options | | {} This. The options = options ∣ ∣ enclosing data = options. The data | | {} / / el Vue instance USES the root of the DOM element to this. El is the root of a Vue instance using the DOM elements This. el is the root DOM element used by Vue instances this.el = typeof options.el === “string”? document.querySelector(options.el) : options.el //2. Convert data members to getters and setters and inject this._proxyData(this.data)//3 into vue instance. NewObserver (this.data) //3 New Observer(this.data)//3 NewObserver (this.data) //4 New Compiler(this)} // Proxy data attribute _proxyData(data){// Traverses all attributes in data object.keys (data).foreach () (key) => {// Inject the data attribute into the Vue instance // in the arrow function, DefineProperty (this, key, {enumerable: true, signals: true, get(){ return data[key] }, set(newValue){ if(newValue === data[key]){ return } data[key] = newValue } }) }) } }
Class 9: the Observer
-
Is responsible for converting properties in the Data option into responsive data
-
An attribute in data is also an object, and the modified attribute is converted into responsive data
-
Send notifications of data changes
-
The defineReactive method in the Observer class passes the third attribute for: When accessing a value in the data property, The set() method in Object.defineProperty in VUE is first fired, and the set() in Object.defineProperty in Vue calls data[key](this.$data), and ob is fired when data[key] is called The get() method in server, which uses data[key] as above without the third argument, is looped
Class Observer {constructor(data){// Call this. Walk (data)} // walk(data){//1. Check whether data is an object or null if(! data || typeof data ! Keys (data). ForEach ((key) => {this.definereactive (data, key); Data [key])})} / / call Object. Defineproperty convert properties to getter and setter definerReactive (obj, key, val) {let that = this / / is responsible for the collection, Let dep = new dep () // When val is an object (internal attributes are not converted to ser() and get()), so we need to call walk to convert this.walk(val). Object.defineProperty(obj, key, { enumerable: true, configurable: True, get(){dep.target && dep.addSub(dep.target) return val}, Set (newValue){if(newValue === val){return} val = newValue // Reassigning the data property to an object also requires the values in the new object as set() and get() this.walk(newValue). // Dep.notify ()},})}}
Ten: the Compiler class
-
Responsible for compiling templates and parsing instruction/differential expressions
-
Responsible for the first rendering of the page
-
Re-render the view when the data changes
Class Compiler {//vm is vue instance constructor(vm){this.el = vm.el this.vm = vm this.compile(this.el)} Compile (el){// Record node let childNodes = el.childNodes array. from(childNodes).foreach ((node) => {// Process text nodes This.iselementnode (node)){this.iselementNode (node)}else if(this.iselementNode (node)){this.iselelement (node)} Compile if(node.childNodes && node.childnodes.length){this.compile(node)}})} Fetch command compileElement(node){// fetch all attribute nodes array.from (node.attributes).foreach ((attr) => {// fetch let attrName = attr.name //text v-text v-model if(this.isDirective(attrName)){ //v-text ==> text attrName = attrName.substr(2) let key = attr.value this.update(node, key, attrName) } }) } update(node, key, AttrName){let updateFn = this.[attrName + ‘Updater’] TextUpdater (node, value, key){node.textContent = value new Watcher(this.vm, Key, (newValue) => {node.textContent = newValue})} key){ node.value = value new Watcher(this.vm, key, (newValue) => {node.value = value}) // Two-way binding Node.adDeventListener (‘input’,() => {this.vm[key] = node.value})} // Compile the text node, CompileText (node){{mag}} let reg = /\{\{(.+?)\}\}/ let value = node.textContent if(reg.test(value)){ 1. Trim () node.textContent = value.replace(reg, New watcher (this.vm, key, (newValue) => {node.textContent = newValue})}} isDirective(attrName){ If not, false return attrname.startswith (‘v-‘)} // Check whether the node is a text node isTextNode(node){//nodeType is 3 rerurn node.nodeType === IsElementNode (node){return node.nodeType === 1}}
Eleven: Dep class
-
Responsible for collecting dependencies, adding Watcher
-
Notify all observers
Class Dep{costructor(){this.subs = []} // Add observer addSub(sub){if(sub && sub.update){ // Add to observer this.subs.push(sub)} // Notify (){this.subs.foreach ((sub) => {// call ypdate sub.update()})}}
Twelve: The Watcher class
Class Watcher{// The first is the Vue instance; the second is the value key; the third is the callback function constructor(vm, key, Cb){this.vm = vm // The data attribute name this.key = key // the callback is responsible for updating the view this.cb = cb // The watcher object is logged to the Dep static attribute target dep. target = this // Trigger the get method, This. OldValue = vm[key] // Set to null to prevent subsequent repetition of dep. target = null} // Update the view when the data changes Update (){let newValue = this.vm[this.key] if(this.oldValue === newValue){return} // Update this.cb(newValue)}}Copy the code