Vue 响应式原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
body
<div id="app">
<div>{{ age }}
<p>
</p>
</div>
<span>{{ name }}</span><br>
<input type="" name="" v-mode="name">
<p>{{ hello }}</p>
</div>
<script type="text/javascript">
const app = document.getElementById('app');
app.getA
class Vue {
constructor(obj={}) {
this.$option = obj;
this.el = obj.el;
this._data = obj.data;
observe(this._data);
for (let key in this._data) {
Object.defineProperty(this, key, {
enumerable: true,
get() {
return this._data[key];
},
set(newVal) {
this._data[key] = newVal;
}
})
}
new Compile(this.el, this);
initComputed.call(this);
}
}
function initComputed() {
let vm = this;
let computed = vm.$option.computed;
vm.computed = computed;
Object.keys(computed).forEach(key => {
Object.defineProperty(vm, key, {
get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
set(){}
})
})
}
const vm = new Vue({
el: '#app',
data: {
name: 'huzhixin',
age: 20,
like: {
name: 'huqiao'
}
},
computed: {
hello() {
return `${this.name}今年${this.age}`;
}
}
})
function Dep () {
this.subs = [];
this.addSub = function(fn) {
this.subs.push(fn);
}
}
Dep.prototype.notify = function () {
this.subs.forEach(item => item.update());
}
function Watcher (vm, reg, fn) {
this.fn = fn;
this.vm = vm;
this.reg = reg;
Dep.target = this;
let arr = reg.split('.');
let val = vm;
arr.forEach(k => {
val = val[k];
})
Dep.target = null;
}
Watcher.prototype.update = function () {
let val = this.vm;
let arr = this.reg.split('.');
arr.forEach(k => {
val = val[k];
})
this.fn(val);
}
function Compile(el, vm) {
vm.$el = document.querySelector(el);
let fragment = document.createDocumentFragment();
while(child = vm.$el.firstChild) {
fragment.appendChild(child);
}
replace(fragment, vm);
vm.$el.appendChild(fragment);
}
function observe(data) {
return new Observe(data);
}
function replace(allNodes, vm) {
Array.from(allNodes.childNodes).forEach(node => {
let text = node.textContent;
let reg = /\{\{(.*)\}\}/;
if (node.nodeType === 1 && reg.test(text)) {
let propArr = RegExp.$1.trim().split('.');
let val = vm;
propArr.forEach(k => {
val = val[k];
})
new Watcher(vm, RegExp.$1.trim(), newVal => {
node.textContent = text.replace(reg, newVal);
})
node.textContent = text.replace(reg, val);
}
if (node.nodeType === 1) {
let nodeAttrs = node.attributes;
Array.from(nodeAttrs).forEach(attr => {
let name = attr.name;
let val = attr.value;
if (name.indexOf('v-model')) {
node.value = vm[val];
}
new Watcher(vm, val, (newVal) => {
node.value = newVal;
})
node.addEventListener('input',function (e) {
let newVal = e.target.value;
vm[val] = newVal;
})
})
}
if (node.childNodes) {
replace(node);
}
})
}
function Observe(data) {
let dep = new Dep();
for (let key in data) {
let val = data[key];
if (val.toString() === '[object Object]') {
observe(val);
} else {
Object.defineProperty(data, key, {
enumerable: true,
get() {
Dep.target && dep.addSub(Dep.target);
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify();
}
})
}
}
}
</script>
</body>
</html>