1. Review Vue responsive usage Vue responsiveness, we are all familiar with it. When we modify the properties in the data object in Vue, the place where the property is referenced in the page will change accordingly. This avoids us having to operate DOM and perform data binding. 2. Analysis of Vue responsive implementationRegarding the responsive principle of Vue, the official website gives a text description https://cn.vuejs.org/v2/guide/reactivity.html. Vue is mainly implemented through data hijacking and observer mode Data Hijacking: vue2.x internally uses Object.defineProperty https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty Proxy used internally by vue3.x https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy Observer pattern: https://www.jb51.net/article/219790.htm Internal member diagram Functions of each member Vue: Inject the members in data into the Vue instance and convert the members in data into getters and setters Observer: Monitor simple type data and objects in the data object and notify Dep when the data changes Compiler: Parse the instruction/difference expression in each element and replace it with the corresponding data Dep: Notifier in observer mode, add observers, and notify observers when data changes Watcher: Each place that references a property in data has a watcher object that is responsible for updating the view Appendix: The properties in the data object act as the observed, and the place where the properties in the data object are referenced acts as the observer 3. Vue responsive source code implementationVue object implementation Function
class Vue{ constructor(options){ // 1. Save the passed-in attributes through attributes this.$options= options||{}; this.$data= options.data||{}; this.$el = typeof options.el ==='string' ? document.querySelector(options.el) : options.el; // 2. Convert the data in the data parameter into getters and setters and mount them on the Vue instance this._proxyData(this.$data) // 3. Call the observe object to monitor changes in data new Observer(this.$data) // 4. Call the compiler object to render the page new Compiler(this) } _proxyData(data){ if (data&&Object.keys(data).length>0){ for (const key in data) { Object.defineProperty(this,key,{ configurable:true, enumerable:true, get(){ return data[key] }, set(value){ if (data[key]===value) { return; } data[key]=value; } }) } } } } Observer object implementation Function
//Data hijacking class Observer {
constructor(data) {
this.walk(data)
}
walk(data) {
//1. Determine whether data is an object if (!data || typeof data !== 'object') {
return
}
//2. Loop call defineReactive to hijack dataObject.keys(data).forEach(key => {
this.defineReactive(data, key, data[key])
})
}
defineReactive(obj, key, val) {
//Create notifier const dep = new Dep()
//Use walk to make the properties in the referenced object responsive this.walk(val)
const that=this;
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get() {
//Notifier collects observers Dep.target && dep.addSub(Dep.target)
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
that.walk(newVal)
//When the observed object changes, the notifier object sends a notification to each observer dep.notify()
}
})
}
} Compile object implementation Function
//Compiler class Compiler {
constructor(vm) {
this.el = vm.$el;
this.vm = vm;
this.compile(this.el)
}
//Compile template to determine whether the node is a text node or an element node compile(el) {
let childNodes = el.childNodes;
//Process the first layer of child nodes Array.from(childNodes).forEach(node => {
if (this.isTextNode(node)) {
this.compileText(node)
} else if (this.isElementNode(node)) {
this.compileElement(node)
}
//If the current node has child nodes, recursively call the compilation instruction if (node.childNodes && node.childNodes.length) {
this.compile(node)
}
})
}
//Compile element node, process instruction compileElement(node) {
//Traverse all instructions Array.from(node.attributes).forEach(attr => {
//Judge whether it is a directive node if (this.isDirective(attr.name)) {
const nodeName = attr.name;
const key = attr.nodeValue;
const directive = nodeName.substr(2)
this.updater(directive,node,key)
}
})
}
updater(directive,node,key){
const updaterFn = this[directive+"Updater"]
updaterFn && updaterFn.call(this,node,this.vm[key],key)
}
//v-text
textUpdater(node,value,key){
node.textContent=value
//The place where v-text expression is used is an observer new Watcher(this.vm,key,newValue => {
node.textContent = newValue
})
}
//v-model
modelUpdater(node,value,key){
node.value =value
//The place where v-model expression is used is an observer new Watcher(this.vm,key,newValue => {
node.value = newValue
})
//Realize two-way bindingnode.addEventListener('input',()=>{
this.vm[key] = node.value
})
}
//v-html
htmlUpdater(node,value,key){
node.innerHTML = value
//The place where v-html expression is used is an observer new Watcher(this.vm,key,newValue => {
node.innerHTML = newValue
})
}
//Processing difference expression compileText(node) {
//Regular expression matching difference let reg = /\{\{(.+?)\}\}/
//Use regular expression to match node's textContent, and replace it if it matches if (reg.test(node.textContent)) {
//Get the key of the interpolation expression
let key = RegExp.$1;
let value = node.textContent;
node.textContent = value.replace(reg, this.vm[key])
//The place where the difference expression is used is an observer new Watcher(this.vm,key,newValue => {
node.textContent = newValue
})
}
}
//Is it a directive isDirective(attrName) {
return attrName.startsWith('v-')
}
//Is it a text node isTextNode(node) {
return node.nodeType === 3
}
//Is it an element isElementNode(node) {
return node.nodeType === 1
}
} Dep object implementation Function
//Notifier class class Dep {
constructor() {
//Storage observer this.subs = []
}
/**
* Collect observers */
addSub(sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
/**
* Notify observers of state changes */
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
} Watcher object implementation Function
//Observer class class Watcher {
constructor (vm,key,cb) {
//Vue instance this.vm =vm;
//key object in data this.key =key;
// Callback function for updating view this.cb = cb
//Store the current observer instance in the target static property of Dep Dep.target = this
//Trigger the getter method of Observe and store the current instance in Dep.subs //The old value corresponding to the key in data this.oldValue = this.vm[this.key]
Dep.target = null
}
//Each observer has an update method to change the state update(){
const newValue = this.vm[this.key]
if ( this.newValue === this.oldValue ) {
return
}
this.cb(newValue)
}
} test <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>index</title> <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> </head> <body> <p id="app"> <h1>Difference Expression</h1> <h3>{{msg}}</h3> <h3>{{count}}</h3> <h1>v-text</h1> <p v-text='msg'></p> <h1>v-model</h1> <input type="text" v-model="msg" attr="msg"> <input type="text" v-model="count"> <h1>v-html</h1> <p v-html="htmlText"></p> </p> <script> let vm = new Vue({ el:"#app", data:{ msg:'information', count:'quantity', person:{name:'张三'}, htmlText:"<p style='color:red'>Hello</p>" } }) </script> </body> This concludes this article about the simple explanation and examples of Vue2.x responsiveness. For more relevant Vue2.x responsiveness content, please search for previous articles on 123WORDPRESS.COM or continue to browse the related articles below. I hope everyone will support 123WORDPRESS.COM in the future! You may also be interested in:
|
<<: Detailed explanation of the initialization mechanism in bash
>>: How to build Nginx image server with Docker
I recently wrote a combination of CSS3 and js, an...
Table of contents 1. Description of functions in ...
Preface In this article, we will continue to expl...
Recently, a database in the production environmen...
Table of contents Layout part: <div id="a...
1. MySql Architecture Before introducing the stor...
Real-time replication is the most important way t...
I have been in contact with PHP for so long, but ...
Pure CSS3 makes a butterfly flapping its wings, s...
Preface During development, we often encounter va...
background I talked to my classmates about a boun...
This article example shares the specific code of ...
Problem Description When we are working on a proj...
As a Vue user, it's time to expand React. Fro...
one. Why build a Nexus private server? All develo...