Vue双向绑定实现原理(二)数据代理和编译模板
2.1.2 模板编译
此时,便可通过vm.属性名获得vm._data.属性名,要实现的功能就是视图和数据连接在一起,数据需要显示在网页上,进行交互,这一步应该就是“数据驱动组件”。
说个题外话,思路到这里,MVVM的架构就非常清晰。
Model VM View
利用VM去连接Model 和 View,VM拥有Model 和 View的实例,也就是匿名对象上的data 和 传递过来的el变量字符串"#app","#app"是一个字符串,它怎么会是View的实例?
vm.$el = document.querySelector("#app")
此时vm拥有了一颗DOM树,只要递归这棵DOM树,就可拥有树下View的节点,便可以操作View。
View 和 Model分离,他们只通过vm去操控对方,而vm在此中扮演的角色就成了一位主持人。
一场表演就是一个作品,一个产品也是一个作品,节目制作的过程中,主持人会去推进整个流程,同时也隔离观众和嘉宾,降低观众和嘉宾的耦合性,毕竟,距离产生美。
当观众需要向嘉宾述说喜欢时,观众会告诉主持人,我很喜欢这个嘉宾,主持人接受到这个信息,告诉嘉宾,嘉宾接受到观众的信息,他很高兴,于是跳了一只舞,这可以理解为视图接受到vm传过来的data数据,展示在页面上。
嘉宾很感谢这位观众的喜欢,但他不知道传递喜欢信息(数据)给他的观众到底是谁,他也不需要知道,他的任务是表演,就像视图的任务是展示数据,与用户交互,而不是获取数据,对数据做一些处理。
同时表演者的观众千千万万,他也不需要关心观众这个实例本身。
表演者只需要通过主持人,主持人去告诉这位观众,表演者很感谢你的喜爱。
观众也得到了来自表演者的信息。
这段废话也是另外一个模式,MVP。
Model View Presenter
Presenter在英文中就是主持的意思,MVP 和 MV VM 意义上是相同的,至少目前小的(微笑)还没发现二者的区别在哪里。
Mustache语法
Vue在进行进行插值处理和 绑定表达式时使用了一种叫Mustache模版引擎,利用双大括号去识别变量,有人称为Mustache语法,双大括号的语法。
不管叫什么,在双大括号里可进行一些语法操作,比如放入变量,也可加入表达式if for之类的,识别这些字符串,做出一些操作,实际上就是编译原理里的词法分析和语法分析。
获取文本节点
在Vue的函数内,添加
// 识别大括号,将文本节点替换
new Compile(this.$options.el, this);
遍历app里的所有节点,事实上需要递归所有节点
function Compile(el, vm) {
vm.$el = document.querySelector(el);
//documentFragment是文档碎片
let fragment = document.createDocumentFragment();
// 这一步是遍历app根节点下的所有节点,因为是DOM树的形式,将节点拆分,挂在新的空白文档上,也就是孩子节点的父节点指向改变了,让fragment作为他们的根节点
while ((child = vm.$el.firstChild)) {
fragment.appendChild(child);
}
// app下的孩子节点挂在fragment下,app下就没有节点,无法显示了了,所以需要显示回来
//replace(fragment);
…………//对fragment进行操作后,重新插回去
vm.$el.appendChild(fragment);
}
createDocumentFragment会创建一个空白文档碎片,
appendChild() 方法可向节点的子节点列表的末尾添加新的子节点。
提示:如果文档树中已经存在了 newchild,它将从文档树中删除,然后重新插入它的新位置。如果 newchild 是 DocumentFragment 节点,则不会直接插入它,而是把它的子节点按序插入当前节点的 childNodes[] 数组的末尾。
创建一个fragment节点,将app下的所有节点断掉,插入fragment节点下,又将fragment插回去,这一步不会多一个fragment节点,因为
newchild 是 DocumentFragment 节点,则不会直接插入它,而是把它的子节点按序插入当前节点的 childNodes[] 数组的末尾
识别双大括号
function replace(fragment) {
Array.from(fragment.childNodes).forEach(function(node) {
let text = node.textContent;
text = text.trim();
let reg = /\{\{(.*)\}\}/;
// 是文本节点 又匹配双大括号语法
if (node.nodeType === 3 && reg.test(text)) {
//打印匹配到的第一个 name.firstName age name
let key = RegExp.$1;
key = key.trim();//去掉首尾空格
let arr = key.split("."); // [name,firstName]
let val = vm; //从vm中去拿
arr.forEach(function(k) {
val = val[k];
});
console.log(val);
// 如果给的就是个对象,那么就序列化显示一下,不然显示【Object object】
if (typeof val === "object") val = JSON.stringify(val);
// 如果不是就直接显示基本数据类型
node.textContent = text.replace(reg, val);
}
// 如果不是文本节点,是标签,则需要遍历标签下面的孩子节点是否有文本节点
if (node.childNodes) {
replace(node);
}
});
}
上一篇: openssl cypto库证书验证(含撤销证书验证)
下一篇: vue实现数据的双向绑定