在vue基本的执行流程存在如下问题:
1) Vue使用的是虚拟DOM
2 )只考虑了单属性 ( {{name}} ),没有考虑层级 ( {{a.b.c.d}} ),而vue中大量的使用层级 ( {{ child.name.firstName }} )
3 )代码没有整合
为了解决上述问题,可以采用如下方法:
1)将真正的DOM和虚拟的DOM相互转换,提供如下方案:
- 采用深拷贝,(递归函数)
- 采用栈结构,使用栈存储父元素来实现递归生成(Vue的源码采用的方式,因为Vue里面是把元素转换为字符串处理)
2)考虑多属性,封装一个通过路径获取对象值的函数。(可采用函数柯里化技巧)
3)可以采用面向对象的方法整合代码。
案例
<div id="root">
<p>
{{name.firstName}}
</p>
<p>
{{a.b.c.d.e}}
</p>
</div>
// 利用了函数柯里化 的技巧,实现获取对象内部属性的值
function createGetValueByPath(path){
let paths = path.split('.');
return function getValueByPath(obj){
let res = obj;
let prop;
while(prop = paths.shift()){
res = res[ prop ];
}
return res;
}
}
//将模板和数据结合起来
function compiler(template, data){
let regKuohao = /\{\{(.+?)\}\}/g; // 匹配{{}}括号
// 把 template 的子元素带有 {{ }} 的给处理一下
let childNodes = template.childNodes; // 子元素
for(let i=0; i < childNodes.length; i++ ){
let type = childNodes[i].nodeType; //节点类型 1 元素 3 文本节点
if( type === 3){
// 文本节点,可以判断里面是否有 {{}} 插值
let txt = childNodes[i].nodeValue; // 该属性只有文本节点才有意义
// 有没有双括号? //正则表达式
txt = txt.replace(regKuohao,function( _ , g1 ){
let path = g1.trim();
// let result = data[path];
let getValueByPath = createGetValueByPath(path); //返回了一个函数
let result = getValueByPath(data)
return result
})
// 注意 txt 现在和 DOM元素 是没有关系的
childNodes[i].nodeValue = txt;
}else if( type === 1){
// 元素,有没有子元素?是否需要将其子元素 进行判断是否插值
compiler(childNodes[i], data)
}
}
}
function JGVue( options ){
// 习惯: 内部的数据使用下划线开头,只读数据使用 $ 开头
this._data = options.data;
this._el = options.el;
//准备工作(准备模板)
this.$el = this._templateDOM = document.querySelector(this._el); // 找到模板
this._parent = this._templateDOM.parentNode;
//渲染工作
this.render()
}
// 原型方法
// 将模板结合数据,得到 HTML, 加到页面中
JGVue.prototype.render = function(){
this.compiler();
}
//编译,,将模板与数据结合得到真正的DOM元素
JGVue.prototype.compiler = function(tepNode){
let realHTMLDOM = this._templateDOM.cloneNode( true ); //克隆模板 //用模板拷贝 得到一个 准DOM
compiler(realHTMLDOM, this._data);
this.update(realHTMLDOM); //替换到页面中
}
//将DOM的元素 放到页面中
JGVue.prototype.update = function(realDom){
this._parent.replaceChild(realDom , document.querySelector(this._el));
}
// 怎么用?
let app = new JGVue({
el:'#root',
data:{
name:{
firstName:'张'
,lastName:'三丰',
},
a:{
b:{
c:{
d:{
e:'哈哈哈'
}
}
}
}
}
})
总结
以上案例考虑了对象多层级属性问题,但是尚未实现虚拟DOM。
下一节将会结合虚拟DOM实现。