vue two-way binding notes

Original: https://github.com/louzhedong/blog/issues/4

 

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<div id="app">
<form>
<input type="text" v-model="number">
<button type="button" v-click="increment">增加</button>
</form>
<h3 v-bind="number"></h3>
</div>
<script>
function myVue(options) {
this._init(options)
}
myVue.prototype._init = function (options) { // 参数 el: '#app',data: {number: 0},methods: {increment: function () {this.number++}}
this.$options = options
this.$el = document.querySelector(options.el) // find the specified element
this.$data = options.data // The data structure in data, such as number:0
this.$methods = options.methods // data structure in methods, such as increment:function(){this.number++}
this._binding = {}
this._obverse(this.$data)
this._complie(this.$el)
}

myVue.prototype._obverse = function (obj) { // data structure, such as number:0
var _this = this
Object.keys(obj).forEach(function (key) { // Object.keys() returns the key of the object
if (obj.hasOwnProperty(key)) { // Whether the object contains the specified property, return boolean
_this._binding[key] = {
_directives: []
}
console.log(_this._binding[key])
var value = obj [key]
if (typeof value === 'object') { // If it is still an object, continue to traverse
_this._obverse(value)
}
var binding = _this._binding[key]
//Define attribute description Parameter 1 defines the object of the attribute, parameter 2 defines the attribute name, and parameter 3 defines the attribute description
Object.defineProperty(_this.$data, key, {
enumerable: true, // enable to appear in the enumeration property of the object
configurable: true, // The property description can be changed only when the property description is turned on, and the property can be deleted at the same time
get: function () { // provide getter methods for properties
console.log(`${key} gets ${value}`)
return value
},
set: function (newVal) { // provide a setter method for the property
console.log(`${key}更新${value}`)
if (value !== newVal) {
value = newVal
binding._directives.forEach(function (item) {
item.update()
})
}
}
})
}
})
}
myVue.prototype._complie = function (root) {
var _this = this
var nodes = root.children
for (var i = 0; i < nodes.length; i++) {
var node = nodes [i]
if (node.children.length) {
this._complie(node)
}
if (node.hasAttribute('v-click')) {
node.onclick = (function () {
var attrVal = nodes[i].getAttribute('v-click')
return _this.$methods[attrVal].bind(_this.$data)
})()
}
if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {
node.addEventListener('input', (function (key) {
var attrVal = node.getAttribute('v-model')
_this._binding[attrVal]._directives.push(new Watcher(
'input',
node,
_this,
attrVal,
'value'
))
return function () {
_this.$data[attrVal] = nodes[key].value
}
})(i))
}
if (node.hasAttribute('v-bind')) {
var attrVal = node.getAttribute('v-bind')
_this._binding[attrVal]._directives.push(new Watcher(
'text',
node,
_this,
attrVal,
'innerHTML'
))
}
}
}

function Watcher(name, el, vm, exp, attr) {
this.name = name // instruction name, such as text node text
this.el = el // directive corresponds to DOM element
this.vm = vm // the myVue instance to which the instruction belongs
this.exp = exp // instruction corresponding value, such as number
this.attr = attr // directive attribute value, such as innerHTML
this.update()
}
Watcher.prototype.update = function () {
this.el[this.attr] = this.vm.$data[this.exp] // When the trigger value changes, the property get method will be called
}
window.onload = function () { // page initialization to create an instance
var app = new myVue({
el: '#app',
data: {
number: 0
},
methods: {
increment: function () {
this.number++
}
}
})
}
</script>
</body>

</html>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325213072&siteId=291194637