2.Compile
Compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body id="app">
<div>
a的值:{{a.a}}
</div>
<p>
b的值 {{b}}
</p>
</body>
<script src="./mvvm.js"></script>
<script>
let vue = new Vue({
el:'#app',
data:{
a:{
a:"是a啊"
},
b:'是b啊'
}
})
console.log(vue)
</script>
</html>
new Vue的时候,会调用 指令解析器,
指令解析器中的方法会把"#app"里面的内容递归处理成文档碎片,放到内存中去,
在内存中,递归文档碎片(虚拟节点),利用正则把“{{}}”替换成data中对应的数据【 此处只是简单实现,没有patch】
然后把内存中被处理后的文档碎片,放回页面,展示出来
function Vue(options={}){
this.$options = options;//将所有属性挂载在options
var 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//这里触发观察者【再次执行Observe(data)中的set方法】
}
})
}
//编译模板【指令解析器】
new Compile(options.el,this)
}
function Compile(el,vm){
//el代表替换的范围
vm.$el = document.querySelector(el);
/*获取到元素以后,把里面的子元素都拿到,需要移动到内存中操作,所以需要创建文档碎片*/
//创建一个虚拟的节点对象,或者说,是用来创建文档碎片节点。它可以包含各种类型的节点,在创建之初是空的。
let fragment = document.createDocumentFragment();
//把app里面的内容都放到内存中,此时页面上为空白
while(child = vm.$el.firstChild){
fragment.appendChild(child)
}
//递归 替换虚拟节点中的 {{}}
replace(fragment)
function replace(fragment){
Array.from(fragment.childNodes).forEach((node)=>{
//循环每一层
let text = node.textContent;
let reg = /\{\{(.*)\}\}/
//nodeType为3时,是文本内容
if(node.nodeType === 3 && reg.test(text)){
//console.log(RegExp.$1) // a.a vm.b
let arr = RegExp.$1.split('.') //[a,a]
let val = vm;
arr.forEach((k)=>{
val = val[k]
})
node.textContent = text.replace(/\{\{(.*)\}\}/,val)
}
if(node.childNodes){
replace(node)
}
})
}
//把内存中的文档碎片放回到页面上,此时页面上的东西显示回来
vm.$el.appendChild(fragment)
}
//vm.$options
function Observe(data){//这里写我们的主要逻辑
for(let key in data){//把data属性通过object.defineProperty的方式定义
let val = data[key]
observe(val)//递归 使data的中的每个属性都被Object.defineProperty处理
Object.defineProperty(data,key,{
enumerable:true,
get(){
return val;
},
set(newVal){//更改值得时候
if(newVal===val){//设置的值和以前是一样的东西
return;
}
val = newVal;//如果以后在获取值得时候,将刚才设置的值丢回去
observe(newVal)
}
})
}
}
function observe(data){
if (typeof data !== 'object'){
return
}
return new Observe(data)
}