从ReactDOM.render开始学react源码

1,625 阅读2分钟

接上:“如何运行react源码” juejin.cn/post/692970…

因为在react项目中,都是从ReactDOM.render开始挂载组件到根节点的,因此render方法应该是学习的一个入口,所以打算从这个方法开始入手;

 // others code ...
 <div id="container"></div>
 
 // others code ...
 ReactDOM.render(
     <h1>Hello World!</h1>,
     document.getElementById('container')
 );

从官网上找到render方法的用法

zh-hans.reactjs.org/docs/react-…

从官网上可以看到render方法的使用方法和功能说明;

找到render方法源码

react-dom源码中的package.json文件中,我们可以找到入口文件为index.js,从index.js中就可以看到render方法,如下图;

源码: 编译后的代码在react-dom.development.js中可以找到:

分析render方法

render的功能是“在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)。”

语法为

ReactDOM.render(element, container[, callback])

源码中可以看到接收的参数,

  • element, 是一个ReactElement对象,babel会将jsx语法的dom编译后经过createElement方法生成一个ReactElement对象;
  • container:放element的容器,容器节点里的所有 DOM 元素都会被替换;
  • callback:回调函数
render(
  element: React$Element<any>,
  container: Container,
  callback: ?Function,
) 

render函数中调用了isValidContainer,isContainerMarkedAsRoot,legacyRenderSubtreeIntoContainer三个方法

isValidContainer

在调用render方法时,首先会通过isValidContainer方法来判断传入的container是不是一个dom元素,判断方法根据dom的nodeType属性处理。

nodeType 属性返回节点类型。

  • 如果节点是一个元素节点,nodeType 属性返回 1。

  • 如果节点是属性节点, nodeType 属性返回 2。

  • 如果节点是一个文本节点,nodeType 属性返回 3。

  • 如果节点是一个注释节点,nodeType 属性返回 8。

  • 该属性是只读的。

具体nodeType值可以点击链接查看:nodeType

源码中,已经定义好常量ELEMENT_NODE,TEXT_NODE等类型值

isContainerMarkedAsRoot

// 生成随机值
const randomKey = Math.random()
  .toString(36)
  .slice(2);
  
// 拼接出key
const internalContainerInstanceKey = '__reactContainer$' + randomKey;

// 判断节点是否有internalContainerInstanceKey
export function isContainerMarkedAsRoot(node: Container): boolean {
  return !!node[internalContainerInstanceKey];
}

legacyRenderSubtreeIntoContainer

将dom节点渲染到container中,这个里面感觉逻辑比较多,后期单独对其中的方法拆开学习。

源码: render中调用该方法:

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

疑问

render方法是react-dom的入口,单独看方法比较简单,+ isValidContainer只是判断是不是正确的节点,这个大家都容易明白;

  • isContainerMarkedAsRoot判断是不是被标记为root的容器,什么时候会被标记?为什么被标记?
  • legacyRenderSubtreeIntoContainer里面包含了很多方法,整个render的核心应该就是它了,react-dom的其他逻辑是不是都是从这个方法开始被调用的?

练手项目

    根据中文网提到的相关操作,可以将react,react-dom打包,直接通过dev.html加载运行,为了后期方便学习,我直接将用到的文件复制到新的空项目中,github地址:github.com/dai12544737…

    如果有想要一起学习的可以直接clone代码,项目中source目录下的文件是打包后直接复制过来的,本人不会修改该目录中内容,packages下是react没有打包前的代码,可以对比学习。

打包后的代码适合本地debug调试,查看react运行机制,可以参考react源码查看。

各位善良的大佬如果发现我学习的方法有问题或者出现了错误,有时间麻烦帮忙指出下,非常感谢~