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 的节点都通过链表连贯:child
,sibling
和return
。
上一篇内容:
React18 技术概览 – 根底篇 https://segmentfault.com/a/11…
下一篇预报:源码实现