React16源码: React中的completeWork对HostComponent处理的源码实现

HostComponent


1 )概述

  • completeWork 当中,我们需要对 HostComponent 的一些操作有哪些?
    • 首先在一次更新而不是初次渲染的情况下
    • 需要去 diffProperties 来计算,需要更新的内容
    • 也就是在 vdom 中去进行一个对比来判断这一个节点是否需要真的去更新它
    • 以此来最低程度的去更新整个 dom 的一个过程
    • 对于不同 dom property,它有一些不同的处理方式

2 )源码

定位到 packages/react-reconciler/src/ReactFiberCompleteWork.js#L581

找到 case HostComponent

case HostComponent: {// 跳过popHostContext(workInProgress);// 跳过const rootContainerInstance = getRootHostContainer();const type = workInProgress.type;// 不是第一次渲染,也就是更新的时候if (current !== null && workInProgress.stateNode != null) {updateHostComponent(current,workInProgress,type,newProps,rootContainerInstance,);if (current.ref !== workInProgress.ref) {markRef(workInProgress); // 跳过}} else {// 第一次挂载渲染// 没有 props 说明有问题,提示if (!newProps) {invariant(workInProgress.stateNode !== null,'We must have new props for new mounts. This error is likely ' +'caused by a bug in React. Please file an issue.',);// This can happen when we abort work.break;}// 跳过const currentHostContext = getHostContext();// TODO: Move createInstance to beginWork and keep it on a context// "stack" as the parent. Then append children as we go in beginWork// or completeWork depending on we want to add then top->down or// bottom->up. Top->down is faster in IE11.let wasHydrated = popHydrationState(workInProgress);if (wasHydrated) {// TODO: Move this and createInstance step into the beginPhase// to consolidate.if (prepareToHydrateHostInstance(workInProgress,rootContainerInstance,currentHostContext,)) {// If changes to the hydrated node needs to be applied at the// commit-phase we mark this as such.markUpdate(workInProgress);}} else {// 第一次挂载// 注意,这里,创建 element的过程let instance = createInstance(type,newProps,rootContainerInstance,currentHostContext,workInProgress,);// 第一次渲染,要添加子节点appendAllChildren(instance, workInProgress, false, false);// Certain renderers require commit-time effects for initial mount.// (eg DOM renderer supports auto-focus for certain elements).// Make sure such renderers get scheduled for later work.if (// 主要功能: 对dom节点上面的可能的事件监听,需要初始化整个react的事件体系finalizeInitialChildren(instance,type,newProps,rootContainerInstance,currentHostContext,)) {markUpdate(workInProgress);}workInProgress.stateNode = instance;}if (workInProgress.ref !== null) {// If there is a ref on a host node we need to schedule a callbackmarkRef(workInProgress);}}break;
}
  • 进入 createInstance
    // packages/react-dom/src/client/ReactDOMHostConfig.js#L167
    // 这个方法就是创建节点的过程
    export function createInstance(type: string,props: Props,rootContainerInstance: Container,hostContext: HostContext,internalInstanceHandle: Object,
    ): Instance {let parentNamespace: string;if (__DEV__) {// TODO: take namespace into account when validating.const hostContextDev = ((hostContext: any): HostContextDev);validateDOMNesting(type, null, hostContextDev.ancestorInfo);if (typeof props.children === 'string' ||typeof props.children === 'number') {const string = '' + props.children;const ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo,type,);validateDOMNesting(null, string, ownAncestorInfo);}parentNamespace = hostContextDev.namespace;} else {parentNamespace = ((hostContext: any): HostContextProd);}// 主要在这里const domElement: Instance = createElement(type,props,rootContainerInstance,parentNamespace,);// 后面会进入这个方法precacheFiberNode(internalInstanceHandle, domElement);// 同上updateFiberProps(domElement, props);return domElement;
    }
    
  • 进入 createElement
    // 不展开这个方法里面调用的内容
    export function createElement(type: string,props: Object,rootContainerElement: Element | Document,parentNamespace: string,
    ): Element {let isCustomComponentTag;// We create tags in the namespace of their parent container, except HTML// tags get no namespace.// 先要获取 document, 先要通过 document 的api来创建const ownerDocument: Document = getOwnerDocumentFromRootContainer(rootContainerElement,);let domElement: Element;let namespaceURI = parentNamespace;// 在React中有区分,不同节点类型,都会有一个定义,比如html普通节点, svg节点等if (namespaceURI === HTML_NAMESPACE) {namespaceURI = getIntrinsicNamespace(type);}// 如果是 html 类型节点,做一些特殊处理if (namespaceURI === HTML_NAMESPACE) {if (__DEV__) {isCustomComponentTag = isCustomComponent(type, props);// Should this check be gated by parent namespace? Not sure we want to// allow <SVG> or <mATH>.warning(isCustomComponentTag || type === type.toLowerCase(),'<%s /> is using incorrect casing. ' +'Use PascalCase for React components, ' +'or lowercase for HTML elements.',type,);}// 对script节点进行特殊处理if (type === 'script') {// Create the script via .innerHTML so its "parser-inserted" flag is// set to true and it does not executeconst div = ownerDocument.createElement('div');div.innerHTML = '<script><' + '/script>'; // eslint-disable-line// This is guaranteed to yield a script element.const firstChild = ((div.firstChild: any): HTMLScriptElement);domElement = div.removeChild(firstChild);} else if (typeof props.is === 'string') {// $FlowIssue `createElement` should be updated for Web ComponentsdomElement = ownerDocument.createElement(type, {is: props.is});} else {// Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.// See discussion in https://github.com/facebook/react/pull/6896// and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240domElement = ownerDocument.createElement(type);// Normally attributes are assigned in `setInitialDOMProperties`, however the `multiple`// attribute on `select`s needs to be added before `option`s are inserted. This prevents// a bug where the `select` does not scroll to the correct option because singular// `select` elements automatically pick the first item.// See https://github.com/facebook/react/issues/13222if (type === 'select' && props.multiple) {const node = ((domElement: any): HTMLSelectElement);node.multiple = true;}}} else {domElement = ownerDocument.createElementNS(namespaceURI, type);}if (__DEV__) {if (namespaceURI === HTML_NAMESPACE) {if (!isCustomComponentTag &&Object.prototype.toString.call(domElement) ==='[object HTMLUnknownElement]' &&!Object.prototype.hasOwnProperty.call(warnedUnknownTags, type)) {warnedUnknownTags[type] = true;warning(false,'The tag <%s> is unrecognized in this browser. ' +'If you meant to render a React component, start its name with ' +'an uppercase letter.',type,);}}}return domElement;
    }
    
  • 进入 precacheFiberNode
    const internalInstanceKey = '__reactInternalInstance$' + randomKey;// 就是在node上挂载上述属性
    // hostInst, node 对应着 internalInstanceHandle, domElement
    // 相当于在 对应dom节点上,通过 internalInstanceKey 这个 key
    // 去创建一个指向 fiber 对象的引用
    // 后期,我们想要从dom对象上获取对应的 fiber就可以方便获取
    export function precacheFiberNode(hostInst, node) {node[internalInstanceKey] = hostInst;
    }
    
  • 进入 updateFiberProps
    const internalEventHandlersKey = '__reactEventHandlers$' + randomKey;// 两个参数 node 和 props, 对应着 domElement, props
    // 在 domElement 上用一个key 存储 props
    // props 会对应到上面 domElement 的 attribute, 所以,有对应关系,方便后期取用
    export function updateFiberProps(node, props) {node[internalEventHandlersKey] = props;
    }
    
  • 进入 appendAllChildren
    appendAllChildren = function(parent: Instance,workInProgress: Fiber,needsVisibilityToggle: boolean,isHidden: boolean,
    ) {// We only have the top Fiber that was created but we need recurse down its// children to find all the terminal nodes.let node = workInProgress.child;// 这个循环的意义:对当前节点下寻找第一层的dom节点或text节点,不会append 嵌套(继续下一层)的dom节点// 这样做的原因,因为对每个dom节点都会有这样一个执行 completeWork 的阶段// 也就是当前层只找自己下一层的dom或text, 一层一层的向下找,不会存在忽略的问题while (node !== null) {if (node.tag === HostComponent || node.tag === HostText) {// 注意这里,挂载节点 // parent 是刚刚创建的 instance// node.stateNode 是当前节点子节点对应的Fiber对象// 如果说,它的子节点那一层上面发现了原生dom节点或者文本节点,就把它挂载到节点上面appendInitialChild(parent, node.stateNode);} else if (node.tag === HostPortal) {// If we have a portal child, then we don't want to traverse// down its children. Instead, we'll get insertions from each child in// the portal directly.} else if (node.child !== null) {// 向下遍历node.child.return = node;node = node.child;continue;}// 这里直接 returnif (node === workInProgress) {return;}// 没有兄弟节点,向上遍历while (node.sibling === null) {if (node.return === null || node.return === workInProgress) {return;}node = node.return;}// 这个本来如此,对sibling进行挂载操作node.sibling.return = node.return;// 循环找 sibling 节点node = node.sibling;}
    };
    
  • 进入 appendInitialChild
    // 这个函数执行的就是 dom 节点的 appendChild方法
    export function appendInitialChild(parentInstance: Instance,child: Instance | TextInstance,
    ): void {parentInstance.appendChild(child);
    }
    
  • 以上,相关注释都在代码内,不再单独拿出详解
  • 里面没涉及到的一些代码,都比较简单,不再赘述
  • 主要关注上面 appendAllChildren 的算法, 内部有注释

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HuoCMS|免费开源可商用CMS建站系统HuoCMS 2.0下载(thinkphp内核)

HuoCMS是一套基于ThinkPhp6.0Vue 开发的一套HuoCMS建站系统。 HuoCMS是一套内容管理系统同时也是一套企业官网建设系统&#xff0c;能够帮过用户快速搭建自己的网站。可以满足企业站&#xff0c;外贸站&#xff0c;个人博客等一系列的建站需求。HuoCMS的优势: 可以使用统一后台…

VSCode Python Windows环境下创建虚拟环境,隔离每个项目的依赖pip包,推荐使用!

VSCode Python Windows环境下创建虚拟环境 Visual Studio Code 可以隔离不同项目的pip依赖包&#xff0c;防止不同版本的干扰**&#xff08;推荐使用&#xff09;** 先在python官网https://www.python.org/downloads/下载需要的python版本&#xff08;我选择了3.9.8&#xff09…

【LeetCode242】有效的字母异位词

242. 有效的字母异位词 数组哈希 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 示例 1: 输入: s “anagram”, t “nagaram” 输出…

小红统计区间(hard) - 树状数组 + 离散化

题面 分析 存在负数不满足单调性&#xff0c;因此无法二分或者双指针&#xff0c;对于每一段符合条件的区间 [ l , r ] [l, r] [l,r] 都有 s u m [ r ] − s u m [ l − 1 ] > k sum[r] - sum[l - 1] > k sum[r]−sum[l−1]>k &#xff0c;也就是 s u m [ l − …

JDK8新特性(一)集合之 Stream 流式操作

1.Stream流由来 首先我们应该知道&#xff1a;Stream流的出现&#xff0c;主要是用在集合的操作上。在我们日常的工作中&#xff0c;经常需要对集合中的元素进行相关操作。诸如&#xff1a;增加、删除、获取元素、遍历。 最典型的就是集合遍历了。接下来我们先举个例子来看看 J…

spring和springboot、springMVC有什么区别?

前言 大家好&#xff0c;我是chowley&#xff0c;今天来聊一下&#xff0c;刚在面试中被问到的一个经典问题 spring和springboot、springMVC有什么区别&#xff1f; Spring、Spring Boot 和 Spring MVC 是 Spring Framework 生态中的不同组件&#xff0c;各自有不同的角色和…

LeetCode-题目整理【6】:除自身以外数组的乘积

除自身以外数组的乘积 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c…

自然语言处理的发展

自然语言处理的发展 自然语言处理&#xff08;NLP&#xff09;是人工智能领域中一门研究如何使计算机理解和生成人类语言的学科。近年来&#xff0c;随着深度学习和大数据技术的突破&#xff0c;NLP 取得了显著的进步&#xff0c;为许多领域带来了革命性的变革。 一、技术进步…

《WebKit 技术内幕》学习之九(2): JavaScript引擎

2 V8引擎 2.1 基础 V8是一个开源项目&#xff0c;也是一个JavaScript引擎的实现。它最开始是由一些语言方面的专家设计出来的&#xff0c;后被Google收购&#xff0c;成为了JavaScript引擎和众多相关技术的引领者。其目的很简单&#xff0c;就是为了提高性能。因为在当时之前…

问题解决:django模型查询报错,找不到数据库表

django项目&#xff0c;使用的postgresql数据库&#xff0c;建了多个模式&#xff0c;模型查询时一直默认查public的表 1. 问题&#xff1a; django.db.utils.ProgrammingError: relation "ip_management_app.table" does not exist 2. 代码&#xff1a; class …

【Java编码】Java原生实现上传下载文件

Java实现上传下载文件 一、上传源码 /*** 1. 上传*/private void upload(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {try {// 1. 创建工厂对象FileItemFactory factory new DiskFileItemFactory();// 2. 文件上传核心…

【学习】focal loss 损失函数

focal loss用于解决正负样本的不均衡情况 通常我们需要预测的正样本要少于负样本&#xff0c;正负样本分布不均衡会带来什么影响&#xff1f;主要是两个方面。 样本不均衡的话&#xff0c;训练是低效不充分的。因为困难的正样本数量较少&#xff0c;大部分时间都在学习没有用…

Linux学习记录——사십사 高级IO(5)--- Epoll型服务器(2)(Reactor)

文章目录 1、完善Epoll简单服务器2、打造统一的分开处理的体系3、epoll工作模式4、ET模式5、继续完善&#xff0c;处理写事件6、引入自定义协议&#xff0c;处理写事件 本篇基于上篇代码继续改进&#xff0c;很长。关于Reactor的说明在后一篇 1、完善Epoll简单服务器 上面的代…

216. 组合总和 III - 力扣(LeetCode)

题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 输入示例 k 3, n 7输出示例 [[1,2,…

深入理解Kubernetes探针和.NET服务健康检查机制

前言 随着越来越多的软件采用云原生和微服务架构&#xff0c;我们面临着更多的技术挑战&#xff0c;比如&#xff1a; Kubernetes如何在容器服务异常终止、死锁等情况下&#xff0c;发现并自动重启服务&#xff1b;当服务依赖的关键服务&#xff08;例如数据库&#xff0c;Red…

【2024-01-22】某极验3流程分析-滑块验证码

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、抓包流程分析1.刷新页面2.点击按钮进行验证3.滑动验证码三、图片还原四、w值①u值②l值③h值④l中的o值aa参…

Dockerfile-xxxx

1、Dockerfile-server FROM openjdk:8-jdk-alpine WORKDIR /app COPY . . CMD java -Xms1536M -Xmx1536M -XX:UseG1GC -jar -Dlog4j2.formatMsgNoLookupstrue -Dloader.pathresources,lib -Duser.timezoneGMT-05 /app/server-main-1.0.0.jar 2、Dockerfile-bgd #FROM openjdk…

8. UE5 RPG创建UI(上)

UI是显示角色的一部分属性玩家可以直接查看的界面&#xff0c;通过直观的形式在屏幕上显示角色的各种信息。如何使用一种可扩展&#xff0c;可维护的形式来制作&#xff0c;这不得不说到耳熟能详的MVC架构。 MVC&#xff08;Model-View-Controller&#xff09;是一种常见的软件…

如何在Linux上部署Nexus私服

如何在Linux上部署Nexus私服 Nexus 是一个强大的仓库管理解决方案&#xff0c;由Sonatype公司开发。它主要用于软件开发中各种依赖包和构件的存储、管理和分发。 1、为什么要部署nexus&#xff1f; 统一管理依赖&#xff1a;在软件开发过程中&#xff0c;项目通常会依赖大量的…

[Linux 杂货铺] —— 权限(文件权限和目录配置)

目录 &#x1f308;前言 &#x1f4c1; 文件的属性 &#x1f4c1; 权限的概念 &#x1f4c2;拥有者和所属组&#xff08;角色&#xff09;&#xff1a; &#x1f4c2;用户&#xff08;具体的人&#xff09;&#xff1a; &#x1f4c1; 权限的管理 &#x1f4c2;1. chmod…