1.Vue的实现原理,相信大家都不陌生了,主要三大函数,Observe, Watcher, Compile,今天记录下手动实现数据双向绑定的功能,直接上代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta data-vue-meta="true" http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
<div id="app">
<input type="text" v-model="msg">
<h3>{{msg}}</h3>
</div>
<script>
function Vue(options = {}) {
this.$options = options
this.$el = document.querySelector(options.el)
this._data = options.data
this._watcher = {}
this._observer(this._data)
this._compile(this.$el)
}
Vue.prototype._observer = function (obj) {
Object.keys(obj).forEach((key) => {
this._watcher[key] = {
_directives: []
}
let watcher = this._watcher[key]
let value = obj[key]
Object.defineProperty(this._data, key, {
configurable: true,
enumerable: true,
get() {
console.log(`${key}获取的值是${value}`)
return value
},
set(newVlue) {
if (newVlue !== value) {
value = newVlue
console.log(`将订阅池里进行对应属性进行处罚更新`)
watcher._directives.forEach(item => {
item.update()
})
}
}
})
})
}
function Watcher(el, vm, val, attr) {
this.el = el
this.vm = vm
this.val = val
this.attr = attr
this.update()
}
Watcher.prototype.update = function () {
this.el[this.attr] = this.vm._data[this.val]
}
Vue.prototype._compile = function (el) {
console.log(el.children)
let nodes = el.children;
const len = nodes.length;
for (let i = 0; i < len; ++i) {
let node = nodes[i];
if (node.children.length) {
// 递归遍历
this._compile(node)
}
// 判断是否有v-mode指令
if (node.hasAttributes('v-model') && node.tagName === 'INPUT') {
node.addEventListener('input', ((key) => {
let attVal = node.getAttribute('v-model')
console.log('attVal', attVal)
// 创建watcher 对象,并且将订阅器根据属性放入对应的订阅器集合
let watcher = new Watcher(node, this, attVal, 'value')
console.log(watcher)
this._watcher[attVal]._directives.push(watcher)
return () => {
this._data[attVal] = nodes[key].value
}
})(i))
}
// 匹配模板
let reg = /\{\{\s*(.*?)\s*\}\}/igs
let txt = node.textContent
if (reg.test(txt)) {
node.textContent = txt.replace(reg, (match, placeholder) => {
// console.log(match,plachoder)
let attrVal = placeholder
let watcher = new Watcher(node, this, attrVal, 'innerHTML')
this._watcher[attrVal]._directives.push(watcher)
return placeholder.split('.').reduce((val, key) => {
return val[key]
}, this._data)
})
}
}
}
</script>
<script>
const vue = new Vue({
el: '#app',
data: {
msg: 'hello world'
}
})
</script>
</body>
</html>
效果如下: