react 源码解析 5.jsx& 外围 api
视频解说(高效学习):进入学习
往期文章:
1. 开篇介绍和面试题
2.react 的设计理念
3.react 源码架构
4. 源码目录构造和调试
5.jsx& 外围 api
6.legacy 和 concurrent 模式入口函数
7.Fiber 架构
8.render 阶段
9.diff 算法
10.commit 阶段
11. 生命周期
12. 状态更新流程
13.hooks 源码
14. 手写 hooks
15.scheduler&Lane
16.concurrent 模式
17.context
18 事件零碎
19. 手写迷你版 react
20. 总结 & 第一章的面试题解答
virtual Dom 是什么
一句话概括就是,用 js 对象示意 dom 信息和构造,更新时从新渲染更新后的对象对应的 dom,这个对象就是 React.createElement()的返回后果
virtual Dom 是一种编程形式,它以对象的模式保留在内存中,它形容了咱们 dom 的必要信息,并且用相似 react-dom 等模块与实在 dom 同步,这一过程也叫协调(reconciler),这种形式能够申明式的渲染相应的 ui 状态,让咱们从 dom 操作中解放出来,在 react 中是以 fiber 树的模式寄存组件树的相干信息,在更新时能够增量渲染相干 dom,所以 fiber 也是 virtual Dom 实现的一部分
为什么要用 virtual Dom
大量的 dom 操作慢,很小的更新都有可能引起页面的重新排列,js 对象优于在内存中,解决起来更快,能够通过 diff 算法比拟新老 virtual Dom 的差别,并且批量、异步、最小化的执行 dom 的变更,以进步性能
另外就是能够跨平台,jsx –> ReactElement 对象 –> 实在节点,有中间层的存在,就能够在操作实在节点之前进行对应的解决,解决的后果反映到实在节点上,这个实在节点能够是浏览器环境,也能够是 Native 环境
virtual Dom 真的快吗?其实 virtual Dom 只是在更新的时候快,在利用初始的时候不肯定快
const div = document.createElement('div');
let str = ''
for(let k in div){str+=','+k}
console.log(str)
jsx&createElement
jsx 能够申明式的形容视图,晋升开发效率,通过 babel 能够转换成 React.createElement()的语法糖,也是 js 语法的扩大。
jsx 是 ClassComponent 的 render 函数或者 FunctionComponent 的返回值,能够用来示意组件的内容,在通过 babel 编译之后,最初会被编译成 React.createElement
,这就是为什么 jsx 文件要申明import React from 'react'
的起因(react17 之后不必导入),你能够在 babel 编译 jsx 站点查看 jsx 被编译后的后果
React.createElement
的源码中做了如下几件事
- 解决 config,把除了保留属性外的其余 config 赋值给 props
- 把 children 解决后赋值给 props.children
- 解决 defaultProps
- 调用 ReactElement 返回一个 jsx 对象(virtual-dom)
//ReactElement.js
export function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
// 解决 config,把除了保留属性外的其余 config 赋值给 props
//...
}
const childrenLength = arguments.length - 2;
// 把 children 解决后赋值给 props.children
//...
// 解决 defaultProps
//...
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE,// 示意是 ReactElement 类型
type: type,//class 或 function
key: key,//key
ref: ref,//ref 属性
props: props,//props
_owner: owner,
};
return element;
};
$$typeof 示意的是组件的类型,例如在源码中有一个查看是否是非法 Element 的函数,就是根 object.$$typeof === REACT_ELEMENT_TYPE 来判断的
//ReactElement.js
export function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}
如果组件是 ClassComponent 则 type 是 class 自身,如果组件是 FunctionComponent 创立的,则 type 是这个 function,源码中用 ClassComponent.prototype.isReactComponent 来区别二者。留神 class 或者 function 创立的组件肯定要首字母大写,不而后被当成一般节点,type 就是字符串。
jsx 对象上没有优先级、状态、effectTag 等标记,这些标记在 Fiber 对象上,在 mount 时 Fiber 依据 jsx 对象来构建,在 update 时依据最新状态的 jsx 和 current Fiber 比照,造成新的 workInProgress Fiber,最初 workInProgress Fiber 切换成 current Fiber。
render
//ReactDOMLegacy.js
export function render(
element: React$Element<any>,//jsx 对象
container: Container,// 挂载 dom
callback: ?Function,// 回调
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
能够看到 render 所做的事也就是调用 legacyRenderSubtreeIntoContainer,这个函数在下一章解说,这里重点关注 ReactDom.render()应用时候的三个参数。
component
//ReactBaseClasses.js
function Component(props, context, updater) {
this.props = props;//props 属性
this.context = context;// 以后的 context
this.refs = emptyObject;//ref 挂载的对象
this.updater = updater || ReactNoopUpdateQueue;// 更新的对像
}
Component.prototype.isReactComponent = {};// 示意是 classComponent
component 函数中次要在以后实例上挂载了 props、context、refs、updater 等,所以在组件的实例上能拿到这些,而更新次要的承载构造就是 updater,次要关注 isReactComponent,它用来示意这个组件是类组件
总结:jsx 是 React.createElement 的语法糖,jsx 通过 babel 转化成 React.createElement 函数,React.createElement 执行之后返回 jsx 对象,也叫 virtual-dom,Fiber 会依据 jsx 对象和 current Fiber 进行比照造成 workInProgress Fiber
pureComponent 也很简略,和 component 差不多,他会进行原型继承,而后赋值 isPureReactComponent
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
export {Component, PureComponent};