数据驱动2_Vue源码_适合小白

解决上一篇不足:

  • Vdom (跟深拷贝类似)
  • 简单整合
  • 属性层级
  • 数据值和路径 (函数柯里化技巧)

为什么Vnode?

一个标签上面的属性何其多个,不断的插入删除势必会造成浏览器卡顿,为了 页面加载速度优化和提高性能Vnode就是用JS对象形式存储dom节点

如:
数据驱动2_Vue源码_适合小白_第1张图片

动态的创建增加无非用到这几个属性: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.标签考虑到子元素,追加

节点一

代码截图:
数据驱动2_Vue源码_适合小白_第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}}


多属性路径值问题

数据驱动2_Vue源码_适合小白_第3张图片

让我们思考下这个问题,当我们要用到对象下某一个属性是,我们在 => 对象.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");

数据驱动2_Vue源码_适合小白_第4张图片

我们来看看 以往我们找 对象.路径 现在 对象以一个参数传进要处理的函数中 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"
        }
      };

你可能感兴趣的:(vue.js,源码分析,源码学习)