React原理记录

深入理解React

虚拟DOM树渲染

虚拟dom这篇文章简单明了的介绍了虚拟DOM,结合Diff算法,这个主题进行整理。

生成Virtual DOM树

虚拟DOM是一个对象,由名称、属性、子节点、key等属性组成,子节点也是虚拟DOM对象,这样就组成了树结构:

{
    // tag的名字
    tagName: 'p',
    // 节点包含属性
    properties: {
        style: {
            color: '#fff'
        }
    },
    // 子节点
    children: [],
    // 该节点的唯一表示,后面会讲有啥用
    key: 1
}

这种对象React中是这样构建的:

// 创建一个div
react.createElement('div', null, [
    // 子节点img
    react.createElement('img', { src: "avatar.png", class: "profile" }),
    // 子节点h3
    react.createElement('h3', null, [[user.firstName, user.lastName].join(' ')])
]);

由Virtual DOM树生成DOM树

这样根据虚拟DOM树,通过调用document.createElement()就可以生成真实的DOM树,简单实现如下:

function create(vds, parent) {
  // 首先看看是不是数组,如果不是数组统一成数组
  !Array.isArray(vds) && (vds = [vds]);
  //  如果没有父元素则创建个fragment来当父元素
  parent = parent || document.createDocumentFragment();
  var node;
  // 遍历所有VNode
  vds.forEach(function (vd) {
    // 如果VNode是文字节点
    if (isText(vd)) {
      // 创建文字节点
      node = document.createTextNode(vd.text);
    } else {
      // 否则创建元素
      node = document.createElement(vd.tag);
    }
    // 将元素塞入父容器
    parent.appendChild(node);
    // 看看有没有子VNode,有孩子则处理孩子VNode
    vd.children && vd.children.length &&
      create(vd.children, node);

    // 看看有没有属性,有则处理属性
    vd.properties &&
      setProps({ style: {} }, vd.properties, node);
  });
  return parent;
}

两个Virtual DOM树的Diff算法

这里做两个树的Diff算法,简单说就是逐级同层比较、并使用key来做优化。

  1. 逐级同层比较

    如图:

diff

​ 这种比较大大降低了复杂度。

  1. key做优化

    key优化

    React会将状态变化前后的组件列表中的每一个组件做一一关联,把状态变化之前的列表作为表1,之后的作为表2。表1的第一个组件关联到表2的第一个组件,依此类推。你可以提供key属性来帮助React找出对应关系。这样就能很快找到修改(新增、替换、删除)的组件,并进行重新渲染。

    ps:这里就涉及到为什么list中index不适合做component的key值。

事件机制

DOM事件

事件机制:冒泡、捕获、委托

DOM事件流(event flow )存在三个阶段:**事件捕获阶段、处于目标阶段、事件冒泡阶段。**s

事件捕获(event capturing):通俗的理解就是,当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件。

**事件冒泡(dubbed bubbling):**与事件捕获恰恰相反,事件冒泡顺序是由内到外进行事件传播,直到根节点。

无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播,它就像一跟引线,只有通过引线才能将绑在引线上的鞭炮(事件监听器)引爆。

示例如下:

<body>
    <div id="parent">
        父元素
        <div id="child">
            子元素
        </div>
    </div>
    <script type="text/javascript">
        var parent = document.getElementById("parent");
        var child = document.getElementById("child");
    
        document.body.addEventListener("click",function(e){
            console.log("click-body");
        },false);
        
        parent.addEventListener("click",function(e){
            console.log("click-parent");
        },false);

        child.addEventListener("click",function(e){
            console.log("click-child");
        },false);
    </script>
</body>

addEventListener(evt、listener、useCapture),前2个参数一个是事件,另外一个是监听函数,最后一个参数为true代表采用捕获方式,为false代表采用冒泡方式,默认为false。

事件委托 : 简单说从父元素的监听函数中,可以响应子元素的事件,子元素的事件处理委托给了父元素。

var parent = document.getElementById("parent");
var child = document.getElementById("child");
parent.onclick = function(e){
            if(e.target.id == "child"){
                console.log("您点击了child元素")
            }
}

这样点击child时,虽然它本身没有监听,但从点击事件的target中,可以返回目标,进而进行响应。

React事件机制

react事件机制 这篇文章介绍的很详细,这里只写一下大致流程。

react 的所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发。

这里有事件注册机制、事件执行机制2种机制。

事件注册机制:

react 事件注册过程其实主要做了2件事:事件注册、事件存储。

a. 事件注册 - 组件挂载阶段,根据组件内的声明的事件类型-onclick,onchange 等,给 document 上添加事件 -addEventListener,并指定统一的事件处理程序 dispatchEvent。

b. 事件存储 - 就是把 react 组件内的所有事件统一的存放到一个对象里,缓存起来,为了在触发事件的时候可以查找到对应的方法去执行

事件执行机制:

在事件注册阶段,最终所有的事件和事件类型都会保存到 listenerBank中。

那么在事件触发的过程中上面这个对象有什么用处呢?其实就是用来查找事件回调

大致流程:

事件触发过程总结为主要下面几个步骤:

1.进入统一的事件分发函数(dispatchEvent)

2.结合原生事件找到当前节点对应的ReactDOMComponent对象

3.开始 事件的合成

​ 3.1 根据当前事件类型生成指定的合成对象

​ 3.2 封装原生事件和冒泡机制

​ 3.3 查找当前元素以及他所有父级

​ 3.4 在 listenerBank查找事件回调并合成到 event(合成事件结束)

4.批量处理合成事件内的回调事件(事件触发完成 end)

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×