2020年11月13日

数据驱动模型(1)

作者 rourou

vue 利用 我们提供的数据 和 页面中模板生成了一个新的HTML标签( Node 元素 ),替换页面中放置模板的位置。vue的执行流程可以通俗的讲分为3个步骤:

1、获得模板:模板中有“坑”

2、利用vue的构造函数中提供的数据来填“坑”,得到可以在页面中显示的“标签”

3、将标签替换原有“坑”的标签

仿写简单的vue的数据驱动(简化版本)的demo,如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="root">
    <p>{{name}}:{{message}}</p>
    <p>{{name}}</p>
    <p>{{message}}</p>
  </div>
  <script>
    // 1、获得模板:模板中有“坑”
    var tmp = document.getElementById('root');

    // 2、利用提供的数据来填“坑”,得到可以在页面中显示的“标签”
    let data = {
      name:'张三',
      message:'今年10岁'
    }

    var rz = /\{\{(.+?)\}\}/g;
    function compiler ( tmp , data){
      var txtNodes = tmp.childNodes;//获取root节点的子节点
      // 判断至子节点是否为文本节点( nodeType )
      for (let i = 0;i < txtNodes.length;i++){
        // 文本节点
        if( txtNodes[i].nodeType == 3){
          // 通过正则判断里面是否有 {{}}
          let txt = txtNodes[i].nodeValue;//nodeValue属性只有再文本节点中才有意义
          // replace 使用正则匹配一次 函数就会被调用一次
          // 函数的第0个参数 表示匹配到的内容
          // 函数的第n个参数 便是正则的 第n组
          txt = txt.replace(rz , function ( _, g ){
            let key = g.trim();
            let val = data [key];
            
            return val;
          })
          // txt 现在和 DOM 元素没有联系,需通过数据赋值
          txtNodes[i].nodeValue = txt; 
        }else if(txtNodes[i].nodeType == 1){
          // 元素节点
          compiler ( txtNodes[i] , data)
        }
      }
    }
    //3、将标签替换原有“坑”的标签
    // 此时没有生成新的template,现在是直接在页面中更新数据,因为DOM是引用类型。产生的影响是更新后原本的模板被覆盖了,解决的办法是拷贝一份。
    var tmpClone = tmp.cloneNode( true );
    compiler ( tmpClone , data );
     tmp.parentNode.replaceChild( tmpClone , tmp );
  </script>
</body>
</html>

第二步骤(代码24-49)是通过递归方式查找DOM节点tmp中的子节点,将数据对应替换到带有双花括号{{}}的文本节点中。

其中查找文本节点中是否含有{{}}采用正则表达式(var rz = /{{(.+?)}}/g;)进行判断,搭配使用replace函数获取数据的对应替换的值,replace函数的第一个参数为匹配到的内容,第n个参数是正则表达是第n组的所匹配到的值(该例子即第二个参数为(.+?)一个组匹配到的值name/message)。最后通过replace函数中return的值赋值给文本节点的nodeValue属性(txtNodes[i].nodeValue = txt;)

上述实例中仿写了vue中数据驱动的demo,需要优化的项如下:

1、vue使用的是虚拟DOM

2、只考虑了单属性 ({{name}}),未考虑多层级关系({{child.name}})

3、代码未整合