react div onclick叠加_自己写个 react

62759c4de6a2feb172e263de563e66b4.png

JSX & React Element

我们在书写 react 代码时,常会写 jsx 代码

const element = (  <div id="container">    <input value="foo" type="text" />    <a href="/bar">bara>    <span onClick={e => alert("Hi")}>click mespan>  div>);

这段代码往往会被 babel 进行编译,转化成如下代码。想了解更多关于 JSX 的知识,可前往 WTF is JSX

const element = createElement(  "div",  { id: "container" },  createElement("input", { value: "foo", type: "text" }),  createElement(    "a",    { href: "/bar" },    "bar"  ),  createElement(    "span",    { onClick: e => alert("Hi") },    "click me"  ));

最后上面的这段代码会生成真正被 react 使用的 React Element,该  React Element 以 Object 形式被 React 进行解析处理

const element = {  type: "div",  props: {    id: "container",    children: [      { type: "input", props: { value: "foo", type: "text" } },      {        type: "a",        props: {          href: "/bar",          children: [{ type: "TEXT ELEMENT", props: { nodeValue: "bar" } }]        }      },      {        type: "span",        props: {          onClick: e => alert("Hi"),          children: [{ type: "TEXT ELEMENT", props: { nodeValue: "click me" } }]        }      }    ]  }};

渲染 DOM 元素

我们常会利用 React.render()  将 JSX 渲染到页面真实 DOM 上,那么这个过程又是如何实现的呢?

我们已经知道 JSX 会被转成 React Element , 接下来就是 React Element 是如何被渲染到 DOM 上

function render(element, parentDom) {  const { type, props } = element;  // Create DOM element  const isTextElement = type === "TEXT ELEMENT"; // 文本类型判定  const dom = isTextElement    ? document.createTextNode("")    : document.createElement(type);  // Add event listeners  const isListener = name => name.startsWith("on");  Object.keys(props).filter(isListener).forEach(name => {    const eventType = name.toLowerCase().substring(2);    dom.addEventListener(eventType, props[name]);  });  // Set properties  const isAttribute = name => !isListener(name) && name != "children";  Object.keys(props).filter(isAttribute).forEach(name => {    dom[name] = props[name];  });  // Render children  const childElements = props.children || [];  childElements.forEach(childElement => render(childElement, dom));  // Append to parent  parentDom.appendChild(dom);}

根据上述代码,可知整个 render 过程大致如下:

  1. 根据 React Element 的 type 字段识别出每个 Element 的类型,根据不同的类型进行相应的处理

  2. 如果元素是普通的 DOM 节点,则添加对应的 DOM 属性、事件监听以及孩子节点

  3. 最后将生成的 DOM 放到页面 container 

DOM DIFF

通过第一部分,我们已经了解到了

  1. JSX 、React Element 以及 JSX 是如何被转化成 React Element 。

  2. React Element 是如何被React利用,并形成 DOM 结构,最终渲染到页面 container  上

接下来,我们要了解下 React 又是如何利用 DOM diff 将不同阶段的 React Element 进行对比,并最终利用 render 函数渲染到页面上

首先,先说明一个概念,React 内部将 DOM diff 的过程称为协调 reconcile , 而每一次 render 其实就是每一次的 reconcile,将旧的 React Element 与 新的 React Element 进行 reconcile ,得到最终的结果

function render(element, container) {  const prevInstance = rootInstance; // 1-虚拟dom主树干- == null  const nextInstance = reconcile(container, prevInstance, element);   rootInstance = nextInstance; // 2-支树干- 领头啦}

reconcile 接收三个参数,分别是容器 constainer、旧的 React Instance、新的 React Element

此处,需要说明下 React Intsance 的概念

React Instance 成为 React 实例,一个 React 实例表示已呈现给DOM的元素,即旧的React Element

它是具有三个属性的纯JS对象:elementdom,和childInstances

function reconcile(parentDom, instance, element) {  if (instance == null) {    // Create instance    const newInstance = instantiate(element);    parentDom.appendChild(newInstance.dom);    return newInstance;  } else if (element == null) { //     // Remove instance    parentDom.removeChild(instance.dom);    return null;  } else if (instance.element.type === element.type) {    // Update instance    updateDomProperties(instance.dom, instance.element.props, element.props);    instance.childInstances = reconcileChildren(instance, element);    instance.element = element;    return instance;  } else {    // Replace instance    const newInstance = instantiate(element);    parentDom.replaceChild(newInstance.dom, instance.dom);    return newInstance;  }}
  1. 首先如果在该 DOM 结构上,如果旧的 React Instance 为空,说明此时不需要 reconcile,直接将新的 React Element 添加到 DOM 结构上即可

  2. 反之,如果新的 React Element 为空,说明该节点要被删除

  3. 如果比较后发现新旧 React Element 的 type 相同,则需要对其孩子节点进行比较

  4. 反之,type 不同,则直接将新的 React Element 替换旧的 React Element 即可

Component State

到此为止,我们已经了解了 JSX 以及 如何将 JSX 通过 DOM diff render 到页面上。

我们知道 React 以组件为基础,那么组件又是如何进行自身的 React Element 更新呢?

我们常以 class App extends React.Component 进行组件的书写,实际上组件的 React Element 更新就是由 React.Component  进行

class Component {  constructor(props) {    this.props = props;    this.state = this.state || {};  }  setState(partialState) {    this.state = Object.assign({}, this.state, partialState);    // 内部实例的引用    updateInstance(this.__internalInstance); // 更新 虚拟-Dom树和 更新 html  }}function updateInstance(internalInstance) {  const parentDom = internalInstance.dom.parentNode;  const element = internalInstance.element;  reconcile(parentDom, internalInstance, element); // 对比-虚拟dom树}

重点就在于 setState 方法

  1. setState 传入当前最新的 state,并将其与旧的 state 进行合并,最终调用 updateInstance 进行更新

  2. updateInstance 获取到组件内部维护的 React Instance,调用 reconcile 进行 DOM Diff ,完成组件的 render

附上 ppt 📎周会showtime-自己写个react.pptx

文章讲述的较为浅显,若想更详细的了解,可前往 didact 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值