react源码笔记(函数级)01

83 阅读2分钟

以下内容纯属个人理解,有错误欢迎评论纠正以及讨论

从最简单的例子开始

下面这个例子是最简单的 单纯的渲染一个hello world 点击变成 Hi world. 从render开始一点点看看react 到底干了啥:)

<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
   <script src="./react.js"></script>
   <script src="./react-dom.js"></script>
</head>

<body>
   <div id="root"></div>
</body>
<script>
   class Hello extends React.Component {
       state={
           text:'Hello'
       }
       change() {
           this.setState({
               text:'Hi'
           })
       }
       render() {
           return React.createElement('div',{onClick:()=>this.change()}, `${this.state.text} ${this.props.toWhat}`);
       }
   }
   ReactDOM.render(
       React.createElement(Hello, { toWhat: 'World' }, null), 
       document.getElementById('root'),
       function(){
           console.log('render callback',this)
       }
   )
</script>

</html>

React.DOM.render

源码

  function render(element, container, callback) {
   if (!isValidContainer(container)) {
     {
       throw Error( "Target container is not a DOM element." );
     }
   }

   {
     var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;

     if (isModernRoot) {
       error('You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOM.createRoot(). This is not supported. ' + 'Did you mean to call root.render(element)?');
     }
   }

   return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
 }

注解

  • element: react element
  • container: 文档root节点
  • callback: 执行完成之后回调 this指向instance 比如示例 指向 <Hello />

这个函数干了啥?

  • 判断container是不是空的
  • 判断是不是绑在document.body下了 react会提示最好不要这么做
  • 判断一下callback参数的合法性
  • 判断一下container._reactRootContainer是否存在来确定是不是第一次mount
  • 判断一下root里面是不是干净的 不是就清除掉

上面的都不重要。。。最关键的是调用了 legacyRenderSubtreeIntoContainer

legacyRenderSubtreeIntoContainer

源码

  function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
   {
     topLevelUpdateWarnings(container);
     warnOnInvalidCallback$1(callback === undefined ? null : callback, 'render');
   } // TODO: Without `any` type, Flow says "Property cannot be accessed on any
   // member of intersection type." Whyyyyyy.


   var root = container._reactRootContainer;
   var fiberRoot;

   if (!root) {
     console.log('第一次渲染')
     // Initial mount
     
     // ❤️ 创建fiberroot ❤️
     root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
     fiberRoot = root._internalRoot;

     // ❤️ 处理callback ❤️
     if (typeof callback === 'function') {
       var originalCallback = callback;

       callback = function () {
         var instance = getPublicRootInstance(fiberRoot);
         originalCallback.call(instance);
       };
     } // Initial mount should not be batched.

     // ❤️ 采取同步运行 ❤️
     unbatchedUpdates(function () {
       updateContainer(children, fiberRoot, parentComponent, callback);
     });
   } else {
     fiberRoot = root._internalRoot;

     if (typeof callback === 'function') {
       var _originalCallback = callback;

       callback = function () {
         var instance = getPublicRootInstance(fiberRoot);

         _originalCallback.call(instance);
       };
     } // Update


     updateContainer(children, fiberRoot, parentComponent, callback);
   }

   return getPublicRootInstance(fiberRoot);
 }

注解

这个函数创建了一个FiberRoot 大概长下面这样
这期间react 还劫持了所有的事件 listenToAllSupportedEvents

image.png

getPublicRootInstance 在浏览器环境下返回的就是containerFiber.child.stateNode,本例子中<Hello />

到这里初始化阶段差不多结束了

初始化阶段(流程图)

image.png

一句话总结

在这个过程中 react 新建了一个fiberRoot,关联上了dom root和fiberRoot,劫持了所有的events。