因为vue主要是通过数据劫持来实现的,通过get、set来完成数据的读取和更新
这里简单实现data,v-model,computed
1模版
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="app">
<div>{{a.a}}</div>
<div>{{c}}</div>
<input type="text" v-model="c">
{{hello}}
</div>
<script src="./mvvm.js"></script>
<script type="text/javascript">
let vue = new Vue2({
el:"#app",
data:{
a:{a:1},
b:[1,2,3],
c:123123
},
computed:{
hello(){
return this.a.a+this.c;
}
}
})
</script>
</body>
</html>
js
function Vue2(options = {}) {
this.$options = options;
let data = this._data = this.$options.data;
observe(data);
// this代理了this._data
for (let key in data) {
Object.defineProperty(this, key, {
enumerable: true,
get() {
return this._data[key];
},
set(newVal) {
this._data[key] = newVal
}
})
}
initComputed.call(this);//初始化computed
new Compile(options.el, this)
}
//计算属性的实现
function initComputed(){
let vm = this;
let computed = this.$options.computed;
Object.keys(computed).forEach(function (k) {
Object.defineProperty(vm,k,{ //如果给的是函数那么调取函数,如果是对象那么获取对象的get方法
get:typeof computed[k] === 'function'?computed[k]:computed[k].get(),
set(){}
})
})
}
function Compile(el, vm) {
// el表示替换的范围
vm.$el = document.querySelector(el);
let frament = document.createDocumentFragment();//文案碎片
while (child = vm.$el.firstChild) {//将app中的内容,移入到内存中
frament.appendChild(child)
}
//查找节点中的{{}}
replace(frament);
function replace(frament) {
Array.from(frament.childNodes).forEach(function (node) {
let text = node.textContent;
let reg = /\{\{(.*)\}\}/;
//判断{{}}
if (node.nodeType === 3 && reg.test(text)) {
console.log(RegExp.$1)//取出花括号中的变量
let arr = RegExp.$1.split('.');//[a,a],[b],[c]
let val = vm;
arr.forEach(function (k) {
val = val[k]
})
new Watcher(vm,RegExp.$1,function (newVal) {
node.textContent = text.replace(/\{\{(.*)\}\}/, newVal)
})
//
node.textContent = text.replace(/\{\{(.*)\}\}/, val)
}
//判断v-model
if(node.nodeType ===1 /*元素节点*/ ){
let nodeAttrs = node.attributes;//获取属性
console.log(nodeAttrs)
Array.from(nodeAttrs).forEach(function (attr) {
console.log(attr)
let name = attr.name;
let exp = attr.value;
if(name.indexOf('v-')==0){
node.value = vm[exp];
}
new Watcher(vm,exp,function (newVal) {
node.value = newVal;
});
node.addEventListener('input',function (e) {
let newVal = e.target.value;
vm[exp] = newVal;
})
})
}
if (node.childNodes) {
replace(node)
}
});
}
vm.$el.appendChild(frament)
}
//观察者 给队形增加Object.defineProperty
// 数据劫持
function Observe(data) {
let dep = new Dep();
for (let key in data) {
let val = data[key];
observe(val);
Object.defineProperty(data, key, {
enumerable: true,
get() {
Dep.target&&dep.addSub(Dep.target);//监控值的变化
return val
},
set(newVal) {
if (newVal === val) {
return
}
val = newVal;
observe(newVal)
dep.notify()//让所有的watch的update方法执行
}
})
}
}
function observe(data) {
if (typeof data !== "object") return;
return Observe(data)
}
//发布订阅
function Dep() {
this.subs = []
}
Dep.prototype.addSub = function (sub) {//订阅
this.subs.push(sub)
}
Dep.prototype.notify = function () {
this.subs.forEach(sub=>sub.update())
}
function Watcher(vm,exp,fn) {
this.vm = vm;
this.exp = exp;
this.fn = fn;
Dep.target = this;
let val = vm;
let arr = exp.split('.')
arr.forEach(function (k) {
val = val[k]
})
Dep.target = null;
}
Watcher.prototype.update=function () {
let val = this.vm;
let arr = this.exp.split('.');
arr.forEach(function (k) {
val = val[k]
})
this.fn(val)
}
来源:https://www.cnblogs.com/caoruichun/p/10801412.html