博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Diy React
阅读量:6642 次
发布时间:2019-06-25

本文共 5429 字,大约阅读时间需要 18 分钟。

babel

在runtime时, babel转译器将对每一个节点都执行在编译注释(Pragma)中声明的函数。 例如:

before babel: (the code you write)

/** @jsx h */let foo = 
Hello!
;复制代码

after babel: (the code you run)

var foo = h('div', {
id:"foo"}, 'Hello!'); 复制代码

也可以在浏览器里打印看一下结果:

  
Document 复制代码

createElement

将jsx中的element转换成 plain object

function createElement(type, config, ...args) {  const props = Object.assign({}, config);  const hasChildren = args.length > 0;  const rawChildren = hasChildren ? [].concat(...args) : [];  props.children = rawChildren    .filter(c => c != null && c !== false)    .map(c => c instanceof Object ? c : createTextElement(c));  return { type, props };}function createTextElement(value) {  return createElement(TEXT_ELEMENT, { nodeValue: value });}复制代码

这里对text node特殊处理了一下,使其不那么特殊, 如此后面处理时省略了许多判断

instance

每个element(jsx转换而来的)对应一个instance,instance包含了dom, element, childInstances。 如果是custom component 则还有一个publicInstance,是组件的实例.

function instantiate(element) {  const { type, props } = element;  const isDomElement = typeof type === "string";  // 这里对custom component与built-in component分别做处理  if (isDomElement) {    const isTextElement = type === TEXT_ELEMENT;    const dom = isTextElement      ? document.createTextNode("")      : document.createElement(type);    // updateDomProperties 作用是更新dom上的属性,比如class、id、或者删除新增事件监听函数等    updateDomProperties(dom, [], props);    // 处理子组件    const childElements = props.children || [];    const childInstances = childElements.map(instantiate);    const childDoms = childInstances.map(childInstance => childInstance.dom);    // 将子组件的实例插入到dom上    childDoms.forEach(childDom => dom.appendChild(childDom));    const instance = { dom, element, childInstances };    return instance;  } else {    const instance = {};    // 或者custom-component 实例    const publicInstance = createPublicInstance(element, instance);    // 调用render方法,获得子组件,再获取child instance.    // 注意这里是childInstance而不是childInstances,因为custom component只能一个子组件,不能是数组    const childElement = publicInstance.render();    const childInstance = instantiate(childElement);    const dom = childInstance.dom;    Object.assign(instance, { dom, element, childInstance, publicInstance });    return instance;  }}复制代码

createPublicInstance

function createPublicInstance(element, internalInstance) {  const { type, props } = element;  const publicInstance = new type(props);  publicInstance.__internalInstance = internalInstance;  return publicInstance;}复制代码

这里的internalInstance其实就是 instance,之所以需要将其绑定到组件实例上是因为 在自定义组件中update需要用到,这里可以先看一下Component的setState实现

setState(partialState) {  this.state = Object.assign({}, this.state, partialState);  updateInstance(this.__internalInstance);}function updateInstance(internalInstance) {  const parentDom = internalInstance.dom.parentNode;  const element = internalInstance.element;  reconcile(parentDom, internalInstance, element);}复制代码

reconciliation 对组件进行 diff

下面instance是当前的状态,而element是新的状态,将这两者进行diff,就可以得出如何更新dom节点

function reconcile(parentDom, instance, element) {  if (instance == null) {    // 不存在instance则创建一个    const newInstance = instantiate(element);    parentDom.appendChild(newInstance.dom);    return newInstance;  } else if (element == null) {    // 不存在element说明该element已经被删除    parentDom.removeChild(instance.dom);    return null;  } else if (instance.element.type !== element.type) {    // 如果新旧element类型不同则替换    const newInstance = instantiate(element);    parentDom.replaceChild(newInstance.dom, instance.dom);    return newInstance;  } else if (typeof element.type === "string") {    // build-in component 更新属性    updateDomProperties(instance.dom, instance.element.props, element.props);    instance.childInstances = reconcileChildren(instance, element);    instance.element = element;    return instance;  } else {    // 更新custom component的属性    instance.publicInstance.props = element.props;    const childElement = instance.publicInstance.render();    const oldChildInstance = instance.childInstance;    const childInstance = reconcile(      parentDom,      oldChildInstance,      childElement    );    instance.dom = childInstance.dom;    instance.childInstance = childInstance;    instance.element = element;    return instance;  }}function reconcileChildren(instance, element) {  const dom = instance.dom;  const childInstances = instance.childInstances;  const nextChildElements = element.props.children || [];  const newChildInstances = [];  const count = Math.max(childInstances.length, nextChildElements.length);  for (let i = 0; i < count; i++) {    const childInstance = childInstances[i];    const childElement = nextChildElements[i];    const newChildInstance = reconcile(dom, childInstance, childElement);    newChildInstances.push(newChildInstance);  }  // 过滤掉已经被删除的element  return newChildInstances.filter(instance => instance != null);}复制代码

Component

由于之前已经讲过了setState,其他部分就很简单了

class Component {  constructor(props) {    this.props = props;    this.state = this.state || {};  }  setState(partialState) {    this.state = Object.assign({}, this.state, partialState);    updateInstance(this.__internalInstance);  }}复制代码

render

function render(element, container) {  reconcile(container, null, element);}复制代码

一个完整的例子

  
Document
复制代码

总结

本文是对文章 的总结。旨在帮助在阅读react源码之前先整体把握react的结构组成,帮助更好的阅读源码。由于react16大量更新了代码,引入了Fiber:Incremental reconciliation,下篇看一下fiber的简单的实现。是同一位作者所写。

参考文章

转载于:https://juejin.im/post/5bcc2f5e6fb9a05d382817b0

你可能感兴趣的文章
Aizu 2300 Calender Colors(暴力)
查看>>
$rootScope
查看>>
Asp.net core 学习笔记 ( DI 依赖注入 )
查看>>
Asp.net core 学习笔记 ( Azure key-vault )
查看>>
[转载] 杜拉拉升职记——17 招人难,求职也难
查看>>
cas系列-cas登出(四)
查看>>
Java练习 SDUT-1149_计算题
查看>>
运行shell脚本的三种方式
查看>>
Java I/O总结
查看>>
学习笔记之scikit-learn
查看>>
用java集合模拟登录和注册功能
查看>>
安装CPqD/ofdissector遭遇的错误
查看>>
C++ 顺序容器
查看>>
Linux下安装ElasticSearch 5 和配置外部访问
查看>>
微信授权登录
查看>>
去除多余样式
查看>>
NuGet Package Explorer使用教程下载
查看>>
还有5个月就NOIP2019了,我干了什么
查看>>
(转)linux下和云端通讯的例程, ubuntu和openwrt下实验成功(二)
查看>>
hdu2707
查看>>