间接进入主题。
const element = <div>copyer</div>
下面的标签语法既不是字符串也是HTML,被称为JSX
,JavaScript的语法扩大。
React中举荐应用JSX
语法。(当然,其余框架也是能够应用的,比方当初的vue3中,也是反对JSX语法的)。
为什么要应用JSX语法?
React 认为渲染逻辑实质上与其余 UI 逻辑外在耦合,比方,在 UI 中须要绑定处理事件、在某些时刻状态发生变化时须要告诉到 UI,以及须要在 UI 中展现筹备好的数据。(官网原话)
那么应用JSX语法,react外部是怎么解析的呢?转化为实在的DOM。
React 16版本
jsx
语法通过babel
转化为React.createElement
函数调用,生成虚构对象。
Babel在线试一试
JSX
模式
function App() {
return <h1 className='ppt'>Hello World</h1>;
}
React.createElement
函数模式
"use strict";
function App() {
return /*#__PURE__*/React.createElement("h1", {
className: "ppt"
}, "Hello World");
}
React.createElement参数剖析
/**
*
* @param {*} type 元素的类型
* @param {*} config 配置对象
* @param {*} children 第一个儿子,如果有多个,顺次放在前面
*/
function createElement(type, config, children) {}
React 17版本
React 17 提供了一个全新的,重构过的 JSX 转换的版本。jsx语法不再化为 React.createElement
函数,而是外部通过 react/jsx-runtime
中jsx
函数生成虚构对象。
官网解释JSX transform
jsx 新的转化,也是通过 babel实现的。两种形式
@babel/plugin-transform-react-jsx
// If you're using @babel/plugin-transform-react-jsx
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"runtime": "automatic"
}]
]
}
@babel/preset-react
// If you are using @babel/preset-react
{
"presets": [
["@babel/preset-react", {
"runtime": "automatic"
}]
]
示例:
jsx
模式
function App() {
return <h1 className='ppt'>Hello World</h1>;
}
new JSX transform
模式
// Inserted by a compiler (don't import it yourself!)
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1',{ className: 'ppt', children: 'Hello world' });
}
比照
createElement
函数与jsx
函数的区别
参数的不同
第一个参数,都是元素的类型。
createElement函数
第二个参数:元素的配置对象; 第三个参数,示意它的第一个子节点,如果有多个子节点,就顺次的从四个参数往下方。(简略的来说,从第三个参数开始,都是元素的子节点)jsx函数
,第二个参数就是一个对象,外面蕴含着元素的配置对象({className: 'aa', children: []}
)。children属性就是示意该元素的子节点。如果只有一个,就是react元素;如果有多个,就是一个数组,外面寄存着所有的子节点。
新的jsx transform
的益处
- 在React16版本,每个组件都必须导入React,不然就会报错。在新的jsx transform中不必导入。(因为新的 JSX 转换会主动引入必要的
react/jsx-runtime
函数,因而当你应用 JSX 时,将无需再引入 React。) - JSX 的编译输入可能会稍微改善 bundle 的大小。
createElement
函数没有废除
只管新的 jsx 编译曾经进去,然而并没有废除 createElement函数。
如果想要应用js
创立元素,还是要应用 createElement。
// 由编译器引入(禁止本人引入!)
import {jsx as _jsx} from 'react/jsx-runtime';
源码剖析
createElement函数源码
packages/react/src/ReactElement.js
// 保留的props
const RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true,
};
// react元素的类型
const REACT_ELEMENT_TYPE = Symbol.for("react.element");
// 判断是不是无效的ref
function hasValidRef(config) {
return config.ref !== undefined;
}
// 判断是不是无效的key
function hasValidKey(config) {
return config.key !== undefined;
}
const ReactCurrentOwner = {
current: null, // (null: null | Fiber)
};
// react元素对象(虚构节点)
const ReactElement = function (type, key, ref, self, source, owner, props) {
const element = {
// react元素的惟一标识
$$typeof: REACT_ELEMENT_TYPE,
// react元素的属性
type: type,
key: key,
ref: ref,
props: props,
// react元素的创建者
_owner: owner,
};
return element;
};
/**
*
* @param {*} type 元素的类型
* @param {*} config 配置对象
* @param {*} children 第一个儿子,如果有多个,顺次放在前面
*/
function createElement(type, config, children) {
let propName;
// 定义props对象
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = "" + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 保留属性,增加到一个新的对象中
for (propName in config) {
// 判断 config的属性是不是保留属性,不是保留属性,就增加到对象中
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName) // reserved_props
) {
props[propName] = config[propName];
}
}
}
// 把children也增加到新的对象中
// 依据函数的参数个数,判断children是否存在
const childrenLength = arguments.length - 2;
// 如果为1,表明有一个children,就是间接赋值给新对象的children属性
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
// 如果有多个儿子,就放到一个数组中,children的value值就是该数组
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
// 去掉后面的两个参数
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 判断该元素有不有type和默认的props(针对类组件)
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props
);
}
jsx函数源码
packages/react/src/ReactJSXElement.js
export function jsx(type, config, maybeKey) {
let propName;
const props = {};
let key = null;
let ref = null;
// maybeKey 就是解决一种key写法的景象
// <div {...props} key='hi'></div> 或则 <div key='hi' {...props}></div>
// props中可能蕴含key,依照第二种写法,props中的key就会本来节点上的key(同一个对象上)
// jsxDEV冀望的是 <div {...props} key='hi'></div>,确定本节点上的key
if (maybeKey !== undefined) {
key = '' + maybeKey;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
if (hasValidRef(config)) {
ref = config.ref;
}
// 过滤掉: 预留的props属性
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
undefined,
undefined,
ReactCurrentOwner.current,
props,
);
}
发现没有,其实两个函数的源码基本上差不多,就是在解决子节点的有点不同。createElement是独自解决的,jsx函数基本就没有解决,间接赋值(因为 children并不预保留属性,所以会间接赋值给props)
总结
jsx被babel解析成虚构对象
。
jsx如果蕴含子节点,子节点也会被解析,顺次上来,就会造成一个深层次的对象,这个对象被称为虚构DOM
。所以虚构DOM就是一个对象。
如果下面写的有误,请指教~~~
发表回复