preface
This paper adopts the observer mode in design mode to simulate the effect of two-way data binding in Vue. If you are not familiar with the observer mode, you can first read “JS design Mode | Observer mode”, this article only simulated the effect of two-way data binding, and Vue source has a big difference
Implementation effect
DOM
Write DOM using Vue syntax rules
2.0 using the Vue
Use native Vue 2.0 to achieve two-way data binding
Analog implementation
myVue
Class USES
myVue
Class implements
MyVue class to achieve the data monitoring of data, DOM simple parsing
The observeddata
The _obverse method transforms data into an observable using the object.defineProperty () method
And register the data object property name as the key of the notification event into the _directives object to notify the bound observer of the update when the value update in data is implemented
Call the myVue class as described in “myVue Class Use”, and the properties in this._directives are shown in the following figure after the this._obverse() method is executed in the myVue class
Watcher class
Declare the Watcher Watcher class to execute the _update() method in the Watcher instance when data is updated to update data on the bound DOM
DOM parsing methods
The _compile() method in myVue is used to parse DOM nodes
Traverse the DOM node
Parse the V-text attribute on the DOM node, bind Watcher listeners for the corresponding node, and update them via the _update() method in Watcher when the listener value changes
The V-model attribute on DOM node is parsed, and input input listener is registered for this node. When input is input, the corresponding value in data is updated, and Watcher listener is bound on this node to update the value to the value attribute of DOM node for value synchronization
Call the myVue class as described in “myVue Class Use”, and the properties of this._directives are shown in the following figure after the this._obverse() and this._compile() methods are executed in the myVue class
The source code
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div class="text" v-text="myText"></div>
<input class="input" type="text" v-model="myText" >
</div>
</body>
<script>
class myVue {
constructor (options) {
this.$el = document.querySelector(options.el)
this.$data = options.data
this._directives = {}
this._obverse(this.$data)
this._compile(this.$el)
}
_obverse (data) {
let _dir
let value
// Iterate over the data object
Object.entries(data).forEach(dataArray= > {
this._directives[dataArray[0]] = [];
value = dataArray[1]
// The value exists and is an object
if(value && value === 'object') {this._obverse(value)
}
_dir = this._directives[dataArray[0]]
Object.defineProperty(this.$data, dataArray[0] and {enumerable: true.configurable: true.get: function() {
return value
},
set: function(newValue) {
// A simple identity comparison is performed without considering complex objects
if(value ! == newValue){ value = newValue _dir.forEach(function(item){
item._update();
})
}
}
})
})
}
_compile (el) {
let nodes = el.children
for(let i = 0; i < nodes.length; i ++){
if(nodes[i].children.length){
this._compile(nodes[i])
}
// Handle the V-text attribute on the DOM
if(nodes[i].hasAttribute('v-text')) {let attrValue = nodes[i].getAttribute('v-text')
this._directives[attrValue].push(new Watcher(nodes[i], this, attrValue, 'innerHTML'))}// Handle v-model attributes on input and textArea
if(nodes[i].hasAttribute('v-model') && (nodes[i].tagName === 'INPUT' || nodes[i].tagName === 'TEXTAREA')) {let attrValue = nodes[i].getAttribute('v-model')
this._directives[attrValue].push(new Watcher(nodes[i], this, attrValue, 'value'))
nodes[i].addEventListener('input'.() = > {
this.$data[attrValue] = nodes[i].value
})
}
}
}
}
class Watcher {
constructor (el, vm, dataKey, elAttr) {
// The DOM node to update
this.el = el
/ / myVue instance
this.vm = vm
// The attribute name of data
this.dataKey = dataKey
// Dom node attribute value
this.elAttr = elAttr
}
_update() {
this.el[this.elAttr] = this.vm.$data[this.dataKey]
}
}
</script>
<script>
const app = new myVue({
el: '#app'.data: {
myText: ' '}})</script>
</html>
Copy the code
conclusion
This article refers to the big guy’s blog for rewriting, interested readers can read through the “resources” jump, give the big old point like. This is the practical application of the observer pattern in JS projects. Through this example, we can further understand the significance of some API design in Vue, such as vue.set () method. As a React developer, I don’t have much experience in Vue. If there are bugs in this article, please kindly comment
The resources
Observer Patterns in JavaScript Design Patterns