JSX

为什么要应用

React 认为渲染逻辑实质上与其余 UI 逻辑外在耦合,比方,在 UI 中须要绑定处理事件、在某些时刻状态发生变化时须要告诉到 UI,以及须要在 UI 中展现筹备好的数据。

React 并没有采纳将标记与逻辑进行拆散到不同文件这种人为地拆散形式,而是通过将二者独特寄存在称之为“组件”的涣散耦合单元之中,来实现关注点拆散

React 不强制要求应用 JSX,然而大多是的时候,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。它还能够使 React 显示更多有用的谬误和正告音讯。

须要关注什么

  • 因为 JSX 语法上更靠近 JavaScript 而不是 HTML,所以 React DOM 应用 camelCase小驼峰命名)来定义属性的名称,而不应用 HTML 属性名称的命名约定。例如,JSX 里的 class 变成了 className,而 tabindex 则变为 tabIndex
  • JSX能够避免注入攻打:React DOM 在渲染所有输出内容之前,默认会进行本义。它能够确保在你的利用中,永远不会注入那些并非本人明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样能够无效地避免 XSS(cross-site-scripting, 跨站脚本)攻打。
  • 能够应用Babel(@babel/preset-react) 把 JSX 转译成一个名为 React.createElement() 函数调用,而不用借助webpack loader等处理器

上面两种写法齐全等价

const element = (  <h1 className="greeting">    Hello, world!  </h1>)
const element = React.createElement(  'h1', /* type */   {className: 'greeting'}, /* props */  'Hello, world!'/* children */);

ReactElement

常用字段和XSS

上面截图是下面代码createElement后的产物,实质上就是个React Object( extends Object):

  • key: 组件的key,次要用在virtual dom上,compare diff和move element
  • props:组件属性,来源于父组件或者HOC,或者相似的内部传递
  • ref:以后的dom援用
  • type: 组件类型
  • _owner: 是React Component,创立react component的组件,空值为null
  • `$$typeof`: 晚期的React(0.13)版本中[很容易](http://danlec.com/blog/xss-via-a-spoofed-react-element)受到 XSS 攻打,为了解决此问题,字段名是应用$$typeof,value应用的是Symbol

React如何解决此问题,通过两种形式:(参考文档 why-do-react-elements-have-typeof-property)

-   编码时:React等新兴代码库会进行本义(客户端)-   数据传输时:因为JSON不反对$$属性名,所以该属性会被过滤掉(服务器端)

Object vs ReactObject

如何辨别object是否是react object?React提供了isValidElement办法

/*** Verifies the object is a ReactElement.* See https://reactjs.org/docs/react-api.html#isvalidelement* @param {?object} object* @return {boolean} True if `object` is a ReactElement.* @final*/export function isValidElement(object) {    return (        typeof object === 'object' &&        object !== null &&        object.$$typeof === REACT_ELEMENT_TYPE    );}

Fiber node

代码中的申明如下

function FiberNode(  tag: WorkTag,  pendingProps: mixed,  key: null | string,  mode: TypeOfMode,) {  // Instance  // 静态数据存储的属性  // 定义光纤的类型。在reconciliation算法中应用它来确定须要实现的工作。如前所述,工作取决于React元素的类型。函数createFiberFromTypeAndProps将React元素映射到相应的光纤节点类型  this.tag = tag;  this.key = key;  this.elementType = null;  // 定义与此光纤关联的性能或类。对于类组件,它指向构造函数,对于DOM元素,它指定HTML标记。我常常应用此字段来理解光纤节点与哪些元素相干。  this.type = null;  // 保留对组件,DOM节点或与光纤节点关联的其余React元素类型的类实例的援用。通常,咱们能够说此属性用于保留与光纤关联的部分状态。  this.stateNode = null;  // Fiber  // Fiber关系相干属性,用于生成Fiber Tree构造  this.return = null;  this.child = null;  this.sibling = null;  this.index = 0;  this.ref = null;  // 动态数据&状态相干属性  // new props,新的变动带来的新的props,即nextProps  this.pendingProps = pendingProps;  // prev props,用于在上一次渲染期间创立输入的Fiber的props  this.memoizedProps = null;  // 状态更新,回调和DOM更新的队列,Fiber对应的组件,所产生的update,都会放在该队列中  this.updateQueue = null;  // 以后屏幕UI对应状态,上一次输出更新的Fiber state  this.memoizedState = null;  // 一个列表,存储该Fiber依赖的contexts,events  this.dependencies = null;    // conCurrentMode和strictMode  // 共存的模式示意这个子树是否默认是 异步渲染的  // Fiber刚被创立时,会继承父Fiber  this.mode = mode;  // Effects  // 以后Fiber阶段须要进行工作,包含:占位、更新、删除等  this.flags = NoFlags;  this.subtreeFlags = NoFlags;  this.deletions = null;  // 优先级调度相干属性  this.lanes = NoLanes;  this.childLanes = NoLanes;  // current tree和working in prgoress tree关联属性  // 在FIber树更新的过程中,每个Fiber都有与其对应的Fiber  // 咱们称之为 current <==> workInProgress  // 在渲染实现后,会指向对方  this.alternate = null;  // 探查器记录的相干工夫  if (enableProfilerTimer) {    // this.actualDuration 真正渲染时长(毫秒级别)    // this.actualStartTime 渲染开始工夫    // this.selfBaseDuration    // this.treeBaseDuration 子树渲染时长     // Note: The following is done to avoid a v8 performance cliff.  }  // 调试相干  if (__DEV__) {    // This isn't directly used but is handy for debugging internals:  }}

类型转化(重点)

React中的每个组件都有一个UI示意模式,咱们能够调用从该render 办法返回的视图或模板。上面的是示例代码ClickCounter

<button key="1" onClick={this.onClick}>Update counter</button><span key="2">{this.state.count}</span>

JSX到ReactElement

模板通过JSX编译器后,您最终将取得一堆React元素。这实际上是从render React组件的办法返回的,而不是HTML。因为咱们不须要应用JSX,因而能够像上面这样重写组件的render 办法ClickCounter

class ClickCounter {    ...    render() {        return [            React.createElement(                'button',                {                    key: '1',                    onClick: this.onClick                },                'Update counter'            ),            React.createElement(                'span',                {                    key: '2'                },                this.state.count            )        ]    }}

办法React.createElement中对的调用render将创立两个数据结构,如下所示:

[    {        $$typeof: Symbol(react.element),        type: 'button',        key: "1",        props: {            children: 'Update counter',            onClick: () => { ... }        }    },    {        $$typeof: Symbol(react.element),        type: 'span',        key: "2",        props: {            children: 0        }    }]

您能够看到React Object中的 $$typeof 属性,它React Element的惟一地标识。而后props 形容的元素属性并传递给React.createElement函数。

ClickCounter的React元素没有任何props 或key:

{    $$typeof: Symbol(react.element),    key: null,    props: {},    ref: null,    type: ClickCounter}

ReactElement到FiberNodes

reconciliation 期间,从render办法返回的每个React元素的数据都将合并到Fiber tree中。每个ReactElement都有一个对应的Fiber Node用来保留组件状态和DOM的可变数据结构。与React元素不同,并不是在每个渲染器上都从新创立Fiber(可复用)。

React中,框架会依据React元素的类型执行不同的流动。在咱们的示例利用中,对于类组件,ClickCounter它调用生命周期办法和render办法,而对于span宿主组件(DOM节点),它执行DOM mutation。因而,每个React元素都被转换为相应类型的Fiber节点,该节点形容了须要实现的工作。

您能够将Fiber视为代表要实现的某些工作或换句话说,一个工作单元的数据结构。Fiber的体系结构还提供了一种不便的形式来跟踪,安顿,暂停和停止工作。

首次将React元素转换为Fiber时,React应用元素中的数据在createFiberFromTypeAndProps函数中创立Fiber。在随后的更新中,React重用了Fiber,并仅应用来自相应React元素的数据来更新必要的属性。key雷同的React元素不再从render办法中返回,React可能还须要依据prop在层次结构中挪动或删除节点。

React为每个React元素创立了一个Fiber node,咱们通过ReactElement Tree能够生成Fiber Tree。在咱们的示例应用程序中,它看起来像这样:

所有Fiber nodes的节点都通过链表连贯:childsiblingreturn

上一篇内容:
React18 技术概览 - 根底篇 https://segmentfault.com/a/11...

下一篇预报:源码实现