找回密码
 会员注册
查看: 33|回复: 0

奇葩说框架之React渲染流程解析

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
63703
发表于 2024-9-19 22:34:18 | 显示全部楼层 |阅读模式
文章背景  本文主要讲两部分内容,一是介绍大致的render函数执行的主流程,包括挂载和渲染jsx节点,对内部调用的函数进行讲解;另一个是细化render阶段做的一些重要内容,包括Fiber的内部计算和任务调度的相关内容。进入正题  我们知道,jsx元素要在页面上渲染出来,首先需要调用React.createElement->React.render(FiberRootNode)->创建更新对象->处理更新队列(任务调度)->进入commit阶段->渲染完成  这是render函数执行的一个主流程  那么,我们先来看看React.createElement函数。React.createElement//JSX代码1// 转换成React.createElement("div", { id: "1"}, "1")createElement函数将会接收三个参数:type一般为DOM节点名称或类组件、函数组件,config中包含着ref,key,props等配置参数,children就是这个DOM节点内部包含的内容,它可能是前面举例的普通元素,也可能是一个数组。在当前函数中都会做一个判断处理。function createElement(type, config, children) {  var propName;   var props = {};  var key = null;  var ref = null;  var self = null;  var source = null;  if (config != null) {    if (hasValidRef(config)) {      ref = config.ref;      {        warnIfStringRefCannotBeAutoConverted(config);      }    }    if (hasValidKey(config)) {      key = '' + config.key;    }    self = config.__self === undefined ? null : config.__self;    source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object    for (propName in config) {      if (hasOwnProperty.call(config, propName) & !RESERVED_PROPS.hasOwnProperty(propName)) {        props[propName] = config[propName];      }    }  }   var childrenLength = arguments.length - 2;  if (childrenLength === 1) {    props.children = children;  } else if (childrenLength > 1) {    var childArray = Array(childrenLength);    for (var i = 0; i , document.getElementById('root')  这行代码告诉React应用,我们想在某个容器中渲染出一个组件。那么我们现在就来看看render这个函数到底有什么内容。  可以看到,它的第一个参数就是前面生成的ReactElement对象。function render(element, container, callback) {  return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);}  render会调用legacyRenderSubtreeIntoContainer进行挂载和更新function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {  var root = container._reactRootContainer;  var fiberRoot;  if (!root) {    // 通过调用legacyCreateRootFromDOMContainer方法将其返回值赋值给container._reactRootContainer    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);    fiberRoot = root._internalRoot;    if (typeof callback === 'function') {      var originalCallback = callback;      callback = function () {        var instance = getPublicRootInstance(fiberRoot);        originalCallback.call(instance);      };    }    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);      };    }    updateContainer(children, fiberRoot, parentComponent, callback);  }  return getPublicRootInstance(fiberRoot);}  当我们第一次启动运行项目的时候,也就是第一次执行ReactDOM.render方法的时候,这时去获取container._reactRootContainer肯定是没有值的,所以我们暂时只关心没有root时的代码处理:  当没有root时,调用legacyCreateRootFromDOMContainer方法将其返回值赋值给container._reactRootContainer,并创建FiberRoot;因为初始化不能批量处理,即同步更新,直接调用unbatchedUpdates方法,这个方法的内容后面再说。function legacyCreateRootFromDOMContainer(container, forceHydrate) {  // 判断是否需要融合  var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);   // 针对客户端渲染的情况,需要将container容器中的所有元素移除  if (!shouldHydrate) {    var warned = false;    var rootSibling;    while (rootSibling = container.lastChild) {      container.removeChild(rootSibling);    }  }  // 返回一个LegacyRoot实例  return createLegacyRoot(container, shouldHydrate ? {    hydrate: true  } : undefined);}  这里会调用shouldHydrateDueToLegacyHeuristic函数去判断是否需要融合,当客户端渲染的情况下,需要将容器里的所有元素移除。结尾处调用createLegacyRoot函数去new一个ReactDOMLegacyRoot实例。function createLegacyRoot(container, options) {  return new ReactDOMLegacyRoot(container, options);}  newReactDOMLegacyRoot的内部会执行createRootImpl函数function ReactDOMLegacyRoot(container, options) {  this._internalRoot = createRootImpl(container, LegacyRoot, options);}  这部分我们要去区分一下FiberRoot和RootFiber的区别和他们之间的联系,往下看function createRootImpl(container, tag, options) {  // Tag is either LegacyRoot or Concurrent Root  var hydrate = options != null & options.hydrate === true;  var hydrationCallbacks = options != null & options.hydrationOptions || null;  var mutableSources = options != null & options.hydrationOptions != null & options.hydrationOptions.mutableSources || null;  var isStrictMode = options != null & options.unstable_strictMode === true;  // 创建一个fiberRoot  var root = createContainer(container, tag, hydrate, hydrationCallbacks, isStrictMode);  // 给container附加一个内部属性用于指向fiberRoot的current属性对应的rootFiber节点  markContainerAsRoot(root.current, container);  var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;  listenToAllSupportedEvents(rootContainerElement);    if (mutableSources) {    for (var i = 0; i 
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-25 13:57 , Processed in 0.299014 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表