间接进入主题。
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
// 保留的propsconst RESERVED_PROPS = { key: true, ref: true, __self: true, __source: true,};// react元素的类型const REACT_ELEMENT_TYPE = Symbol.for("react.element");// 判断是不是无效的reffunction hasValidRef(config) { return config.ref !== undefined;}// 判断是不是无效的keyfunction 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就是一个对象。
如果下面写的有误,请指教~~~