Skip to content

Instantly share code, notes, and snippets.

@oHaiyang
Last active June 12, 2017 01:28
Show Gist options
  • Save oHaiyang/13b068866e404adcf17e0c736fce8a05 to your computer and use it in GitHub Desktop.
Save oHaiyang/13b068866e404adcf17e0c736fce8a05 to your computer and use it in GitHub Desktop.
一个模仿的模仿 Vue 的双向绑定实现
<!-- 参考原链接: 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