说实话走了一遍其实有的地方还是不太懂有1说1
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="./Vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<h2> {{name}}</h2>
<div>
<h3>{{age}}</h3>
</div>
<input v-model="name" name="" id="" value="" />
</div>
</body>
<script type="text/javascript">
const app = new Vue({
el:"#app",
data:{
name:"11",
age:"22",
info:{
a:"as",
b:"ad2w"
}
},
methods:function(){
}
})
console.log(app)
</script>
</html>
vue.js
class Vue {
constructor(options) {
this.$data = options.data;
//调用数据劫持
Observe(this.$data);
//属性代理 直接在 Vue实例上获取属性 少去.$data在点属性的取值
Object.keys(this.$data).forEach((key)=>{
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get: function() {
return this.$data[key]
},
set: function(newVal) {
this.$data[key] =newVal;
}
});
})
//调用模板编辑
compile(options.el,this);
}
}
// 定义一个数据劫持方法
function Observe(obj){
//递归的终止条件
if(!obj || typeof obj !="object") return;
const dep = new Dep();
//获取到对象上的每个属性
let objkey = Object.keys(obj);
objkey.forEach((key)=>{
//当前循环属性对应的值
let value = obj[key];
//再次递归循环对象里面的属性
Observe(value);
//为每一个属性添加上getter 和setter属性
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
//返回对应的值
console.log(`有人获取了$(key)的值`)
//刚才new 的Watcher实例就被存放到了 dep.subs这个数组中
Dep.target && dep.addSub(Dep.target);
return value;
},
set: function(newVal) {
console.log(`有人设置了值$(newVal)`)
value= newVal;
Observe(value);
//通知每个订阅者更新自己
dep.notify()
}
});
})
}
//编译模板
function compile(el,vm){
//获取dom元素 挂载到$el身上
vm.$el = document.querySelector(el);
//创建一个文档碎片,提高dom 效率
const xunidom = document.createDocumentFragment();
while((childNode = vm.$el.firstChild)){
xunidom.appendChild(childNode)
}
//进行模板编译
repalcetemp(xunidom)
//在渲染到页面上
vm.$el.appendChild(xunidom);
//负责对dom模板进行编辑
function repalcetemp(node){
//定义插值正则表达式
const regMustache = /\{\{\s*(\S+)\s*\}\}/;
//证明当前文本节点node 节点是一个文本节点 需要正则替换
if(node.nodeType ===3){
const text = node.textContent;
//进行正则匹配提取
const execResult = regMustache.exec(text);
if(execResult){
const value = execResult[1].split(".").reduce((nreobj,key)=>nreobj[key],vm)
node.textContent = text.replace(regMustache,value);
//在这个时候 创建Watcher 类的实例
new Watcher(vm, execResult[1],(newValue)=>{
node.textContent = text.replace(regMustache,newValue)
});
}
return
}
//判断当前Node节点是否是input输入框
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,(newValue)=>{
node.value =newValue;
});
//监听输入事件 更新值 同步dao vm上
node.addEventListener("input",(e)=>{
const val = e.target.value;
const keyArr= expstr.split(".");
const obj = keyArr.slice(0,keyArr.length-1).reduce((newobj,k)=>newobj[k],vm)
obj[keyArr[keyArr.length-1]] = val;
})
}
}
//递归调用
node.childNodes.forEach((child)=>repalcetemp(child))
}
}
//收集依赖 收集订阅
class Dep{
constructor() {
this.subs=[];
}
//像subs 中添加订阅信息
addSub(watcher){
this.subs.push(watcher)
}
//发布订阅方法 负责通知每个watcher 的方法
notify(){
this.subs.forEach((watcher)=>watcher.update())
}
}
//订阅的类
class Watcher{
constructor(vm,key,cb) {
//cb 回调函数中,记录 Watcher如何更新自己的文本内容
this.cb=cb;
this.key=key;
this.vm=vm;
// 负责把创建的Watcher 实例存储到Dep实例的subs数组中
Dep.target =this;
key.split(".").reduce((newobj,k)=> newobj[k],vm)
Dep.target = null;
}
//触发回调
update(){
const val = this.key.split(".").reduce((newobj,k)=> newobj[k],this.vm);
this.cb(val);
}
}