iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >一文帮你理解PReact10.5.13源码
  • 452
分享到

一文帮你理解PReact10.5.13源码

2024-04-02 19:04:59 452人浏览 泡泡鱼
摘要

目录render.js部分create-context.js部分diff部分React源码看过几次,每次都没有坚持下来,索性学习一下PReact部分,网上讲解源码的不少,但是基本已经

React源码看过几次,每次都没有坚持下来,索性学习一下PReact部分,网上讲解源码的不少,但是基本已经过时,所以自己来梳理下

render.js部分


import { EMPTY_OBJ, EMPTY_ARR } from './constants';
import { commitRoot, diff } from './diff/index';
import { createElement, Fragment } from './create-element';
import options from './options';


export function render(vnode, parentDom, replaceNode) {
 if (options._root) options._root(vnode, parentDom);

 // We abuse the `replaceNode` parameter in `hydrate()` to signal if we are in
 // hydration mode or not by passing the `hydrate` function instead of a DOM
 // element..
 let isHydrating = typeof replaceNode === 'function';

 // To be able to support calling `render()` multiple times on the same
 // DOM node, we need to obtain a reference to the previous tree. We do
 // this by assigning a new `_children` property to DOM nodes which points
 // to the last rendered tree. By default this property is not present, which
 // means that we are mounting a new tree for the first time.
  // 为了支持多次在一个dom节点上调用render函数,需要在dom节点上添加一个饮用,用来获取指向上一次渲染的虚拟dom树。
  // 这个属性默认是指向空的,也意味着我们第一次正在装备一颗新的树
  // 所以开始时这里的oldVNode是空(不论isHydrating的值),但是如果重复在这个节点上调用render那oldVNode是有值的
 let oldVNode = isHydrating
  ? null
  : (replaceNode && replaceNode._children) || parentDom._children;

 // 用Fragment包裹一下vnode,同时给replaceNode和parentDom的_children赋值
  vnode = (
  (!isHydrating && replaceNode) ||
  parentDom
 )._children = createElement(Fragment, null, [vnode]);

 // List of effects that need to be called after diffing.
  // 用来放置diff之后需要进行各种生命周期处理的Component,比如cdm、cdu;componentWillUnmount在diffChildren的unmount函数中执行不在commitRoot时执行
 let commitQueue = [];
 diff(
  parentDom, // 这个使用parentDom的_children属性已经指向[vnode]了
  // Determine the new vnode tree and store it on the DOM element on
  // our custom `_children` property.
  vnode,
  oldVNode || EMPTY_OBJ, // 旧的树
  EMPTY_OBJ,
  parentDom.ownerSVGElement !== undefined,
    // excessDomChildren,这个参数用来做dom复用的作用
  !isHydrating && replaceNode
   ? [replaceNode]
   : oldVNode
   ? null
   : parentDom.firstChild // 如果parentDom有子节点就会把整个子节点作为待复用的节点使用
   ? EMPTY_ARR.slice.call(parentDom.childNodes)
   : null,
  commitQueue,
    // oldDom,在后续方法中用来做标记插入位置使用
  !isHydrating && replaceNode
   ? replaceNode
   : oldVNode
   ? oldVNode._dom
   : parentDom.firstChild,
  isHydrating
 );

 // Flush all queued effects
  // 调用所有commitQueue中的节点_renderCallbacks中的方法
 commitRoot(commitQueue, vnode);
}


export function hydrate(vnode, parentDom) {
 render(vnode, parentDom, hydrate);
}

create-context.js部分

Context的使用:

Provider的props中有value属性

Consumer中直接获取传值


import { createContext, h, render } from 'preact';

const FontContext = createContext(20);

function Child() {
 return <FontContext.Consumer>
 {fontSize=><div style={{fontSize:fontSize}}>child</div>}
 </FontContext.Consumer>
}
function App(){
 return <Child/>
}
render(
 <FontContext.Provider value={26}>
 <App/>
 </FontContext.Provider>,
 document.getElementById('app')
);

看一下源码:


import { enqueueRender } from './component';

export let i = 0;

export function createContext(defaultValue, contextId) {
 contextId = '__cC' + i++; // 生成一个唯一ID

 const context = {
  _id: contextId,
  _defaultValue: defaultValue,
  
  Consumer(props, contextValue) {
   // return props.children(
   //  context[contextId] ? context[contextId].props.value : defaultValue
   // );
   return props.children(contextValue);
  },
  
  Provider(props) {
   if (!this.getChildContext) { // 第一次调用时进行一些初始化操作
    let subs = [];
    let ctx = {};
    ctx[contextId] = this;
       
       // 在diff操作用,如果判断一个组件在Comsumer中,会调用sub进行订阅;
       // 同时这个节点后续所有diff的地方都会带上这个context,调用sub方法进行调用
       // context具有层级优先级,组件会先加入最近的context中
    this.getChildContext = () => ctx; 

    this.shouldComponentUpdate = function(_props) {
     if (this.props.value !== _props.value) {
      // I think the forced value propagation here was only needed when `options.debounceRendering` was being bypassed:
      // https://GitHub.com/preactjs/preact/commit/4d339fb803bea09e9f198abf38ca1bf8ea4b7771#diff-54682ce380935a717e41b8bfc54737f6R358
      // In those cases though, even with the value corrected, we're double-rendering all nodes.
      // It might be better to just tell folks not to use force-sync mode.
      // Currently, using `useContext()` in a class component will overwrite its `this.context` value.
      // subs.some(c => {
      //  c.context = _props.value;
      //  enqueueRender(c);
      // });

      // subs.some(c => {
      //  c.context[contextId] = _props.value;
      //  enqueueRender(c);
      // });
            // enqueueRender最终会进入renderComponent函数,进行diff、commitRoot、updateParentDomPointers等操作
      subs.some(enqueueRender);
     }
    };

    this.sub = c => {
     subs.push(c);// 进入订阅数组,
     let old = c.componentWillUnmount;
     c.componentWillUnmount = () => { // 重写componentWillUnmount
      subs.splice(subs.indexOf(c), 1);
      if (old) old.call(c);
     };
    };
   }

   return props.children;
  }
 };

 // Devtools needs access to the context object when it
 // encounters a Provider. This is necessary to support
 // setting `displayName` on the context object instead
 // of on the component itself. See:
 // Https://reactjs.org/docs/context.html#contextdisplayname
 // createContext最终返回的是一个context对象,带着Provider和Consumer两个函数
 // 同时Consumber函数的contextType和Provider函数的_contextRef属性都指向context
 return (context.Provider._contextRef = context.Consumer.contextType = context);
}

所以对于Provider组件,在渲染时会判断有没有getChildContext方法,如果有的话调用得到globalContext并一直向下传递下去


if (c.getChildContext != null) {
    globalContext = assign(assign({}, globalContext), c.getChildContext());
   }

   if (!isNew && c.getSnapshotBeforeUpdate != null) {
    snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
   }

   let isTopLevelFragment =
    tmp != null && tmp.type === Fragment && tmp.key == null;
   let renderResult = isTopLevelFragment ? tmp.props.children : tmp;

   diffChildren(
    parentDom,
    Array.isArray(renderResult) ? renderResult : [renderResult],
    newVNode,
    oldVNode,
    globalContext,
    isSvg,
    excessDomChildren,
    commitQueue,
    oldDom,
    isHydrating
   );

当渲染遇到Consumer时,即遇到contextType属性,先从Context中拿到provider,然后拿到provider的props的value值,作为组件要获取的上下文信息。同时这时候会调用provider的sub方法,进行订阅,当调用到Provider的shouldComponentUpdate中发现value发生变化时就会将所有的订阅者进入enqueueRender函数。

所以源码中,globalContext对象的每一个key指向一个Context.Provider;componentContext代表组件所在的Consumer传递的上下文信息即配对的Provider的props的value;

同时Provider的shouldComponentUpdate方法中用到了 ·this.props.value !== _props.value· 那么这里的this.props是哪来的?Provider中并没有相关属性。

主要是下面这个地方,当判断没有render方法时,会先用Compoent来实例化一个对象,并将render方法设置为doRender,并将constructor指向newType(当前函数),在doRender中调用this.constructor方法


// Instantiate the new component
    if ('prototype' in newType && newType.prototype.render) {
     // @ts-ignore The check above verifies that newType is suppose to be constructed
     newVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap
    } else {
     // @ts-ignore Trust me, Component implements the interface we want
     newVNode._component = c = new Component(newProps, componentContext);
     c.constructor = newType;
     c.render = doRender;
    }


function doRender(props, state, context) {
 return this.constructor(props, context);
}

diff部分

diff部分比较复杂,整体整理了一张大图

真是不得不吐槽,博客园的编辑器bug太多了,尤其是Mac上使用,比如第二次上传代码提交不了;赋值粘贴用不了。。。

只有情怀让我继续在这里更新

到此这篇关于一文帮你理解PReact10.5.13源码的文章就介绍到这了,更多相关PReact10.5.13源码内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 一文帮你理解PReact10.5.13源码

本文链接: https://www.lsjlt.com/news/122838.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 一文帮你理解PReact10.5.13源码
    目录render.js部分create-context.js部分diff部分React源码看过几次,每次都没有坚持下来,索性学习一下PReact部分,网上讲解源码的不少,但是基本已经...
    99+
    2024-04-02
  • 一篇带你解析入门LongAdder源码
    目录1、LongAdder由来2、LongAdder与AtomicLong的简单介绍3、AtomicLong3.1 AtomicLong实现原理3.2 AtomicLong瓶颈分析4...
    99+
    2024-04-02
  • 一文带你认识python源文件中的字符编码
    今天就跟大家聊聊有关一文带你认识python源文件中的字符编码,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动...
    99+
    2023-06-06
  • 用代码帮你了解Python基础(1)
    目录1.数据类型和变量2.字符串和格式化总结1.数据类型和变量 # 数据类型: # 1.整型:Python中能处理任意大小的整数 intNum1 = 584520 intNum2 =...
    99+
    2024-04-02
  • 用代码帮你了解Python基础(3)
    目录1.循环2.字典3.集合总结1.循环 # 1.for...in循环,依次把list或tuple中的每个元素迭代出来 studentNames = ["Willard","Chen...
    99+
    2024-04-02
  • 用代码帮你了解Python基础(2)
    目录1.列表:list2.元组:tuple3.条件判断总结1.列表:list # 1.list:Python内置的一种数据类型,列表; # 2.list是一种有序的集合,可以随时添加...
    99+
    2024-04-02
  • 一文带你快速理解git
    随着软件开发的快速发展,版本控制也逐渐成为一个重要的话题。Git是目前最受欢迎的版本控制系统之一,它为程序员提供了一种有效的方法来跟踪代码的更改历史记录、协作开发以及管理不同的版本,尽管Git对于初学者可能略显复杂,但只要你按照以下步骤可以...
    99+
    2023-10-22
  • 源码解析带你了解LinkedHashMap
    目录元素存储关系继承体系属性构造方法无参有参按插入顺序访问newNodelinkNodeLast链表节点的删除LRU(Least recently used,最近最少使用)栗子元素被...
    99+
    2024-04-02
  • RocketMQ broker文件清理源码解析
    目录1. broker 清理文件介绍1.1 哪些文件需要清理1.2 RocketMQ文件清理的机制2. 源码解析2.1 清理commitlog2.2 ConsumeQueue 清理2...
    99+
    2023-05-14
    RocketMQ broker文件清理 RocketMQ broker
  • 一文带你深入理解GolangContext包
    目录1. 基本原理1.1 Context 包的介绍1.2 Context 的创建1.2.1 WithCancel1.2.2 WithDeadline1.2.3 WithTimeout...
    99+
    2023-05-18
    Golang Context包 Golang Context
  • 一文详解Electron电源状态管理
    目录Electron 电源相关模块其中 powerMonitor 模块提供的接口powerSaveBlocker 模块提供的方法空闲状态监控电源状态监控锁屏和解锁休眠和唤醒系统行为阻...
    99+
    2023-03-10
    Electron 电源状态管理 Electron 状态管理
  • 一篇文章带你理解ReactProps的原理
    目录props理解1)props 可以是:2)props在React充当角色(3个角度):3)监听props改变:操作 props1、抽象 props1)混入 props2)抽离 p...
    99+
    2024-04-02
  • 一篇文章带你从java字节码层理解i++和++i
    目录程序目的关键指令i++示例源码使用jclasslib查看i++字节码++i示例源码参考总结程序目的 从java字节码层理解,为何i = i++后,结果是+1之前的数值。而i=++...
    99+
    2024-04-02
  • 一篇文章教你如何理解SQL
    这篇文章主要介绍了一篇文章教你如何理解SQL,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。SQL 是一种为数不多的声明性语言,它的运行方式完...
    99+
    2024-04-02
  • Java Jersey框架资源详解,帮你轻松掌握API开发
    Java Jersey框架介绍 Java Jersey框架是一个用于构建RESTful Web服务的开源框架,它提供了对JAX-RS API的支持,并简化了RESTful API的开发工作。Jersey框架是一个轻量级的框架,它只需要很...
    99+
    2024-02-27
    Java Jersey框架 JAX-RS RESTful Web服务 RESTful API
  • yocto-sumo源码解析(一): o
    oe-init-build-env是yocto构建环境运行的第一个脚本,通过运行下面的命令:   . oe-init-build-env build-arm64 即可对yocto项目进行构建,顾名思义,该脚本主要目的还在于给yocto项目...
    99+
    2023-01-30
    源码 yocto sumo
  • 一文带你深入理解Golang中的RWMutex
    目录RWMutex 的整体模型读操作模型写操作模型基本用法一个简单的例子RWMutex 使用的注意事项源码剖析RWMutex 结构体读锁源码剖析写锁源码剖析TryRLock 和 Tr...
    99+
    2023-05-14
    Golang RWMutex原理 Golang RWMutex使用 Golang RWMutex
  • 一文带你深入理解Vue3响应式原理
    目录 响应式原理2.0的不足reactive和effect的实现effect track trigger测试代码递归实现reactive总结 响应式原理 Vue2...
    99+
    2022-11-13
    vue3响应式原理精讲 vue3 响应式 vue 响应式原理
  • 文件同步遇到难题?PHP Spring 帮你解决!
    文件同步是很多开发者和 IT 人员经常面临的问题。如果你曾经遇到过文件同步的难题,那么你一定知道它有多么烦人。当你需要将文件从一个地方同步到另一个地方时,它往往会花费大量的时间和精力,而且容易出现一些问题。但是,有了 PHP Spring,...
    99+
    2023-08-06
    spring 文件 同步
  • 了解 VSCode 如何帮助你更好地编写代码
    标题:探索VSCode如何助你更优秀编程技巧 在当今的数字化时代,编程已经成为了人们生活和工作中不可或缺的技能。而一款优秀的代码编辑器则是每位开发者必备的利器。在众多的代码编辑器中,V...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作