示例图:
1. html部分
<body>
<div id="app">
<h3>name: {{name}}</h3>
<h3>info.a: {{info.a}}</h3>
<div>name的值:<input type="text" v-model="name"></div>
<div>info.a的值:<input type="text" v-model="info.a"></div>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data:{
name: 'zs',
age: 20,
info:{
a:'a1',
b:'b1'
}
}
})
console.log(app);
</script>
</body>
2. js部分
class Vue{
constructor(options){
this.$data = options.data;
Observer(this.$data);
Object.keys(this.$data).forEach((key) => {
Object.defineProperty(this,key, {
enumerable: true,
configurable: true,
get(){
return this.$data[key];
},
set(newVal){
this.$data[key] = newVal;
}
})
});
Compile(options.el, this);
}
}
function Observer(obj){
if (!obj || typeof obj !== 'object') return
const dep = new Dep();
Object.keys(obj).forEach((key) => {
let value = obj[key];
Observer(value);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get(){
Dep.target && dep.addSub(Dep.target);
return value;
},
set(newVal){
value = newVal;
Observer(value);
dep.notify()
}
})
})
}
function Compile(el, vm){
vm.$el = document.querySelector(el);
const fragment = document.createDocumentFragment();
while((childNode = vm.$el.firstChild)){
fragment.appendChild(childNode);
}
replace(fragment);
vm.$el.appendChild(fragment);
function replace(node){
const regMustache = /\{\{\s*(\S+)\s*\}\}/;
if (node.nodeType === 3) {
const text = node.textContent;
const execResult = regMustache.exec(text);
if (execResult) {
const value = execResult[1].split('.').reduce((newObj,key) => newObj[key], vm);
node.textContent = text.replace(regMustache, value);
new Watcher(vm, execResult[1], (newVal)=> {
node.textContent = text.replace(regMustache, newVal);
})
}
return;
}
if (node.nodeType === 1 && node.tagName.toUpperCase() === 'INPUT') {
const attrs = Array.from(node.attributes);
const findResult = attrs.find((x) => x.name === 'v-model');
if (findResult) {
const expStr = findResult.value;
const value = expStr.split('.').reduce((newObj,k) => newObj[k], vm);
node.value = value;
new Watcher(vm, expStr, (newVal) => {
node.value = newVal;
});
node.addEventListener('input', (e) => {
const keyArr = expStr.split('.');
const obj = keyArr.slice(0, keyArr.length - 1).reduce((newObj, k) => newObj[k], vm);
const leafKey = keyArr[keyArr.length - 1];
obj[leafKey] = e.target.value;
})
}
}
node.childNodes.forEach((child) => replace(child));
}
}
class Dep{
constructor(){
this.subs = [];
}
addSub(watcher){
this.subs.push(watcher);
}
notify(){
this.subs.forEach((watcher) => watcher.update());
}
}
class Watcher{
constructor(vm, key, cb){
this.vm = vm;
this.key = key;
this.cb = cb;
console.log(key);
Dep.target = this;
key.split('.').reduce((newObj, k) => newObj[k], vm);
Dep.target = null;
}
update(){
const value = this.key.split('.').reduce((newObj, k) => newObj[k], this.vm);
this.cb(value);
}
}
|