Last active
June 12, 2017 01:28
-
-
Save oHaiyang/13b068866e404adcf17e0c736fce8a05 to your computer and use it in GitHub Desktop.
一个模仿的模仿 Vue 的双向绑定实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- 参考原链接: http://www.cnblogs.com/kidney/p/6052935.html?utm_source=gold_browser_extension --> | |
<div id="app"> | |
<input type="text" v-model="text"/> | |
{{text}} | |
</div> | |
<script> | |
function compile(dom, vm) { | |
const frag = document.createDocumentFragment(); | |
for (var i = 0, l = dom.childNodes.length; i < l; i++) { | |
var node = dom.childNodes[i]; | |
if (node.nodeType === 1) { | |
var model = node.attributes['v-model'] | |
if (model) { | |
node.value = vm.data.text; | |
node.addEventListener('input', function (e) { | |
vm.data.text = e.target.value; | |
}) | |
node.removeAttribute('v-model'); | |
} | |
} else if (node.nodeType === 3) { | |
const regRes = node.nodeValue.trim().match(/\{\{(.+)\}\}/) | |
const dataName = regRes ? regRes[1] : '' | |
if (dataName in vm.data) { | |
new Watcher(node, vm, dataName); | |
} | |
} | |
} | |
} | |
function defineReactive(obj, key, value) { | |
console.log('defining'); | |
var dep = new Dep(); | |
Object.defineProperty(obj, key, { | |
set (newValue) { | |
console.log('setter is running', value); | |
if (value === newValue) return false; | |
value = newValue; | |
dep.notify(); | |
}, | |
get () { | |
console.log('getter is running', value); | |
if (Dep.target) dep.addSub(Dep.target); | |
return value; | |
}, | |
}) | |
} | |
function observe (vm) { | |
Object.keys(vm.data).forEach(key => { | |
defineReactive(vm.data, key, vm.data[key]); | |
}) | |
} | |
function Dep () { | |
this.target = null; | |
this.subs = []; | |
} | |
Dep.prototype.addSub = function (sub) { | |
this.subs.push(sub); | |
} | |
Dep.prototype.notify = function () { | |
console.log('notifing'); | |
this.subs.forEach(sub => sub.update()) | |
} | |
function Watcher (node, vm, dataName) { | |
Dep.target = this; | |
this.dataName = dataName; | |
this.node = node; | |
this.vm = vm; | |
this.update(); | |
Dep.target = null; | |
} | |
Watcher.prototype.update = function () { | |
this.get(); | |
console.log('uuuupdate', this.node, this.value); | |
this.node.nodeValue = this.value; | |
} | |
Watcher.prototype.get = function () { | |
// 在针对每个 node,new Watcher 的时候,这个函数被首次调用,而此时 `vm.data[this.dataName]` 上的 getter 被触发, | |
// 而在此时刻,Dep.target 是指向目标 node 的,因此 `vm.data[this.dataName]` 在 defineReactive 的时候,getter | |
// 实际以闭包的方式,拥有一个 Dep 实例 dep,getter 发现 Dep.target 有值,就将其加到 dep.subs 上了。至此,每次 `vm.data[this.dataName]` | |
// 的 setter 被调用时,就会通过同样的通过闭包获得访问能力的 dep 实例,通知更改。 | |
this.value = this.vm.data[this.dataName]; | |
} | |
function Vue (vm) { | |
this.el = vm.el | |
this.data = vm.data | |
observe(vm); | |
const dom = document.getElementById(vm.el); | |
compile(dom, vm); | |
} | |
var vm = new Vue({ | |
el: 'app', | |
data: { | |
text: 'Hello World', | |
} | |
}) | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment