解决上一篇不足:
- Vdom (跟深拷贝类似)
- 简单整合
- 属性层级
- 数据值和路径 (函数柯里化技巧)
为什么Vnode?
一个标签上面的属性何其多个,不断的插入删除势必会造成浏览器卡顿,为了
页面加载速度优化和提高性能
Vnode就是用JS对象形式存储dom节点
动态的创建增加无非用到这几个属性:nodeValue,nodeName,nodeType,attributes
..
Vdom:
用构造函数的形式:
class Vnode {
constructor(tag, data, value, nodeType) {
this.tag = tag && tag.toLowerCase(); //=>1
this.data = data;
this.value = value;
this.nodeType = nodeType;
this.child = []; //=>2
}
appendChild(vnode) {
this.child.push(vnode);
}
}
function getVnode(node) {
let type = node.nodeType;
let _vnode = null;
if (type === 1) { //=>3
let nodeName = node.nodeName;
let attri = node.attributes;
let attr = {}; //=>4
for (let i = 0; i < attri.length; i++) {
attr[attri[i].nodeName] = attri[i].nodeValue;
}
_vnode = new Vnode(nodeName, attr, undefined, type); //=>5
let childNodes = node.childNodes; //=>6
for (let i = 0; i < childNodes.length; i++) {
_vnode.appendChild(getVnode(childNodes[i]));
}
} else if (type === 3) { //=>1.tag不可能为undefined所以要tag存在并且转换为小写
_vnode = new Vnode(undefined, undefined, node.nodeValue, type);
}
return _vnode;
}
let node = document.querySelector("#app");
let vnode = getVnode(node);
console.log(vnode);
//=>2.标签考虑到子元素,追加
节点一
细说:
1.tag就是getVnode
传的如果dom节点是文本节点
那么tag就是undefined
2.child为数组是因为标签元素有子元素
,所以childNodes
有子元素所以调用实例下的appendChild方法并递归
3.检验type类型,如果是元素节点就有tag和data也就是属性比如class 和 id,当取出attributes这是个伪数组
4.js以对象存储,所以要把伪数组转换成对象存储
5.开始实例化对象,元素节点就传tag的参数,文本节点就传文本节点的参数
6.如果是节点元素看看是否有子节点
简单整合
let NameObj={
hobby:'ilove',
name:{
firstName:'i',
lastName:'mycode'
}
}
let vm = new ivue({
el:'#app',
data:data
})
function ivue(options){
this._data=options.data;
this._el=options.el;
//模板和data拿到
this._tmp=document.querySelector(this._el);
this._parent=this._tmp.parentNode; //=>body
this.render();
}
ivue.prototype.render=function(){
this.compiler();
}
ivue.prototype.compiler=function(){
let realHtmlDOM=this._tmp.cloneNode(true);
compiler(realHtmlDOM,this._data);
this.uopdate(realHtmlDOM);
}
ivue.prototype.update=function(real){
this._parent.replaceChild(real,document.querySelector('#app'))
}
var regExp = /\{\{(.+?)\}\}/g; //=>定义验证字符串规则
function compiler(template, data) {
var childNodes = template.childNodes; //=>1
for (let i = 0; i < childNodes.length; i++) {
var type = childNodes[i].nodeType; //=>2
if (type === 3) { //=>3
let txt = childNodes[i].nodeValue;
txt = txt.replace(regExp, (_, g) => {
let key = g.trim(); //=>4
let value = data[key]; //=>5
return value;
}); //=>6
childNodes[i].nodeValue = txt; //=>7
} else if (type === 1) { //=>8
compiler(childNodes[i], data);
}
}
}
{{hobby}}
多属性路径值问题
让我们思考下这个问题,当我们要用到对象下某一个属性是,我们在 =>对象.path
通过.
路径下一直直到找到所要的对象名,那么在vue里是如何实现多层路径访问呢?其实这里需要用到函数柯里化技巧
function getValueByPath(O, path) { //=>O就是对应的`对象`
let paths = path.split(".");
let res = O;
let prop;
while ((prop = paths.shift())) {
res = res[prop];
}
return res;
}
let value = getValueByPath(objName, "name.lastName");
我们来看看 以往我们找对象
.路径 现在 对象以一个参数传进要处理的函数中O
替代 我们通过split('.')截取
按.
截则会得到一个数组
类似['name','firstName','lastName']然后我们通过循环的方式,使得res=O[name] =>objName[name] =>{firstName:'..',lastName:'..'} 如果作为结果在循环得 firstName 这要多亏了prop得功劳每次我们都会deep进入更深层
柯里化:
柯里化技巧,vue中data是无时无刻不在改变得而路径path只要写了就是死得所以我们可以这么做,先写path,这样一来我们直接在编译得时候就执行了,在全局可用
function createValueByPath(path) {
let paths = path.split(".");
return function getValueByPath(O) {
let res = O;
let prop;
while ((prop = paths.shift())) {
res = res[prop];
}
return res;
};
}
let getValueByPath = createValueByPath("name.firstName");
let value = getValueByPath(objName);
document.write(value);
let objName = {
name: {
firstName: "MrsBob",
lastName: "LiVison"
}
};