我们知道在 React 实际是通过虚拟 DOM 来生成真实的 DOM, React 对 JavaScript 进行了扩展,使得 React 有能力在 JavaScript 中属性类 HTML 代码,让开发人员更加具有亲近感,但是这里,这里想要做的事看清楚这种亲近感的本质 --- JavaScript 的 DOM 描述。
创建一个 React 元素
- API: React.createElement(component, props, ...children)
import React from 'react'
// 一个 React 组件
function App(props) {
return (
<div>{props.name}</div>
)
}
// 渲染这个组件到指定的 DOM
ReactDOM.render(<App />, document.getElementById('app'))
经过 Babel 编译之后
import React from 'react'; // 一个 React 组件
function App(props) {
return /*#__PURE__*/React.createElement("div", null, props.name);
} // 渲染这个组件到指定的 DOM
ReactDOM.render( /*#__PURE__*/React.createElement(App, null), document.getElementById('app'));
React.createElement 的实现
React.createElement / ReactEleme…, 它处理 props, 然后调用了 ReactElement 生成一个 React 元素
export function createElement(type, config, children) {
let propName;
const props = {};
// other codes
if (config != null) {
// other codes
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
// __DEV__ codes
props.children = childArray;
}
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
// __DEV__ codes
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
示例
- 示例1: 嵌套传递 props
import React from 'react'
import ReactDOM from 'react-dom'
const h = React.createElement
const Comp2 = function Hello (props) {
const { name } = props
return h('div', null, `my name is ${name}`)
}
let Comp1 = h(Comp2, {name: 'tom'}, null)
ReactDOM.render(Comp1, document.getElementById('app'))
- 示例2: 没声明函数的隐式函数组件, children 中含有变量
const name = 'Josh Perez';
const h = React.createElement
const element = /*#__PURE__*/h("h1", null, "Hello, ", name);
ReactDOM.render(element, document.getElementById('root'));
- 示例3: children 中含有 函数
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const h = React.createElement
const element = /*#__PURE__*/h("h1", null, "Hello, ", formatName(user), "!");
ReactDOM.render(element, document.getElementById('root'));
- 示例4:函数组件中含有条件语句
const h = React.createElement
function getGreeting(user) {
if (user) {
return /*#__PURE__*/h("h1", null, "Hello, ", formatName(user), "!");
}
return /*#__PURE__*/h("h1", null, "Hello, Stranger.");
}
- 示例5: 隐式函数组件中的属性
const h = React.createElement
const element1 = /*#__PURE__*/h("div", {
tabIndex: "0"
});
const element2 = /*#__PURE__*/h("img", {
src: user.avatarUrl
});
- 示例6: 多个子元素
const h = React.createElement
const element = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h1", null, "Hello!"), /*#__PURE__*/React.createElement("h2", null, "Good to see you here."));
- 示例7: 一个类组件的编译
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// 编译后
const h = React.createElement
class Welcome extends React.Component {
render() {
return /*#__PURE__*/h("h1", null, "Hello, ", this.props.name);
}
}
- 示例8: 一个相对复杂的函数组件
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
// 编译后
const h = React.createElement
function Comment(props) {
return /*#__PURE__*/h("div", {
className: "Comment"
}, /*#__PURE__*/h("div", {
className: "UserInfo"
}, /*#__PURE__*/h("img", {
className: "Avatar",
src: props.author.avatarUrl,
alt: props.author.name
}), /*#__PURE__*/h("div", {
className: "UserInfo-name"
}, props.author.name)), /*#__PURE__*/h("div", {
className: "Comment-text"
}, props.text), /*#__PURE__*/h("div", {
className: "Comment-date"
}, formatDate(props.date)));
}
这种复杂的组件,看起来嵌套就不容易看懂了,在写这种组件时,我们最好将子组件中的内容单独提取出来,使得逻辑简单。
这个时候模板的优势就显现出来了, 但是这个问题也是有解决办法,下面就是两个由社区提供的解决方案,然我们在 h 函数的时候,能够更加的舒畅...
React 社区的解决方案
- react-hyperscript React.js标记的超脚本语法
- hyperscript-helpers hyperscript 的简介语法
对比 Vue 中的 $createElment
我们知道 Vue 中也可以使用 render 函数来编写 Vue 组件,render 函数的本质其实就是调用了 Vue 实例下的 $createElement 方法,与 React 思想上其实大同小异,都是使用了创建元素,然后使用虚拟 DOM 对比 DOM,到真实的 DOM 的过程。
Vue 中不使用模板 文章讲解了不使用 Vue 模板的使用方法
参考
本文使用 mdnice 排版