React系列文章:无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程。
先以下面一段简单的代码举例:
const Greeting = function ({name}) {
return <div>{`hello ${name}`}</div>;
};
const App = <Greeting name="scott"/>;
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
可以看出,Greeting是一个无状态组件,我们来看看编译过后的可执行代码:
var Greeting = function Greeting(_ref) {
var name = _ref.name;
return React.createElement(
"div",
null,
"hello " + name
);
};
var App = React.createElement(Greeting, { name: "scott" });
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
我们看到,调用Greeting组件时传入的name属性,出现在React.createElement()方法的第二个参数中,这和前面介绍的JSX是一致的,不同的是,React.createElement()方法的第一个参数不再是一个标签名,而是一个函数引用,指向了我们声明的Greeting组件,而name属性也作为参数的成员出现在组件内部,这个参数名为_ref,实则是我们熟知的props。
下图是我们运行上面代码之后,打印出的App数据结构,即虚拟DOM结构:

我们再来看一个稍微复杂些的例子:
const Greeting = function ({name}) {
return (
<div>{`hello ${name}`}</div>
);
};
const Container = function ({children}) {
return (
<div className="container">
{children}
</div>
);
};
const App = (
<Container>
<Greeting name="scott"/>
<Greeting name="jack"/>
<Greeting name="john"/>
</Container>
);
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
在上面代码中,我们定义了两个无状态组件,其中Container用来作为外层的容器,Greeting则用来显示实际的业务视图。
现在再来看看编译后的代码结构:
var Greeting = function Greeting(_ref) {
var name = _ref.name;
return React.createElement(
"div",
null,
"hello " + name
);
};
var Container = function Container(_ref2) {
var children = _ref2.children;
return React.createElement(
"div",
{ className: "container" },
children
);
};
var App = React.createElement(
Container,
null,
React.createElement(Greeting, { name: "scott" }),
React.createElement(Greeting, { name: "jack" }),
React.createElement(Greeting, { name: "john" })
);
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
这次我们主要观察Container的结构,它实际上是将React.createElement()方法的第三个参数作为props.children传递到了组件内部,而这个children是一个Greeting,最终是将Greeting渲染在Container组件内部。
接下来,我们要改进一下之前实现的React.createElement()和ReactDOM.render()方法,使它们支持组件的形式,模拟生成虚拟DOM和真实DOM。
先来看看React.createElement()方法:
const React = {
// 创建DOM描述对象 即虚拟DOM
createElement(type, props, ...children) {
let propsChildren = children;
// 组件参数的props.children本身是数组
// 所以调用组件函数时这里需要特殊处理
if (Array.isArray(children[0])) {
propsChildren = children[0];
}
// 结点
let vnode = {
type,
props: {
...props,
children: propsChildren,
}
};
// 挂载组件函数体的虚拟DOM
if (typeof type === 'function') {
vnode.body = type({
...props,
children,
});
}
return vnode;
}
};
上面的代码主要对组件做了特殊处理。如果当前处理对象是组件,则对应的type就是函数的引用,我们会调用这个组件函数,然后将函数体的结果作为body属性挂载到该结点上。需要注意的是,我们在上面方法参数中使用了可变参数的形式,如果直接引用这个children,它本身就是一个变参数组,如果组件体内使用了props.children,那么在调用React.createElement()时,变参数组的形式将会是[[...]],所以我们需要特殊处理一下。
现在,我们运行程序,看看上面代码生成的虚拟DOM结构:

最后,再来看看ReactDOM.render()方法:
const ReactDOM = {
// 渲染真实DOM
render(vnode, container) {
let realDOM = this.generateDOM(vnode);
container.appendChild(realDOM);
},
// 获取真实DOM
generateDOM(vnode) {
if (typeof vnode.type === 'function') {
// 将组件函数体的虚拟DOM生成真实DOM
return this.generateDOM(vnode.body);
}
let elem = document.createElement(vnode.type);
// 特殊key值映射
let specialKeyMap = {
className: 'class',
fontSize: 'font-size',
};
let {props} = vnode;
// 设置DOM属性
props && Object.keys(props).forEach(key => {
if (key === 'children') {
// 处理子节点
props.children.forEach(child => {
if (typeof child === 'string') {
// 纯内容节点
elem.appendChild(document.createTextNode(child));
} else {
// DOM节点
elem.appendChild(this.generateDOM(child));
}
});
} else if (key === 'style') {
// 设置样式属性
let styleObj = props.style;
let styleItems = [];
Object.keys(styleObj).forEach(styleKey => {
styleItems.push(`${specialKeyMap[styleKey] || styleKey}:${styleObj[styleKey]}`);
});
elem.setAttribute('style', styleItems.join(';'));
} else {
// 设置其他属性
elem.setAttribute(specialKeyMap[key] || key, props[key]);
}
});
return elem;
}
};
上面代码中改动较小,我们只添加了几行针对组件的处理逻辑,如果是组件函数,则将函数体的虚拟DOM生成真实DOM。
最后,我们来看看最终生成的DOM结构:

React系列文章:无状态组件生成真实DOM结点的更多相关文章
- React: 无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了 JSX 生成真实 DOM 结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({ ...
- React: 有状态组件生成真实DOM结点
上次我们分析了无状态组件生成 DOM 的过程,无状态组件其实就是纯函数,它不维护内部的状态,只是根据外部输入,输出一份视图数据.而今天我们介绍的有状态组件,它有内部的状态,因此在组件的内部,可以自行对 ...
- react的redux无状态组件
Provider功能主要为以下两点: 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件 接收Redux的store作为props,通过context对象传递给子孙组件上的connec ...
- React系列文章:JSX生成真实DOM结点
在上一篇文章中,我们介绍了Babel是如何将JSX代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了Babel编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const style ...
- React: JSX生成真实DOM结点
在上一篇文章中,我们介绍了 Babel 是如何将 JSX 代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了 Babel 编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const ...
- react 中的无状态函数式组件
无状态函数式组件,顾名思义,无状态,也就是你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展示性组件,接收Props,渲染DOM,而不关注其他逻辑. 其实无状态函数式组件也是 ...
- React 中的 Component、PureComponent、无状态组件 之间的比较
React 中的 Component.PureComponent.无状态组件之间的比较 table th:first-of-type { width: 150px; } 组件类型 说明 React.c ...
- React中的高阶组件,无状态组件,PureComponent
1. 高阶组件 React中的高阶组件是一个函数,不是一个组件. 函数的入参有一个React组件和一些参数,返回值是一个包装后的React组件.相当于将输入的React组件进行了一些增强.React的 ...
- 15. react UI组件和容器组件的拆分 及 无状态组件
1.组件的拆分 组件拆分的前提 当所有的逻辑都出现在一个组件内时 组件会变得非常复杂 不便与代码的维护 所以对组件进行拆分 IU组件 进行页面渲染 容器组件 进行逻辑操作 UI组件的拆分 新建一个 ...
随机推荐
- OA协同办公软件
OA协同办公软件: 泛微软件. 九思软件. 华天动力. 万户OA.:北京万户网络技术有限公司创立于1998年2月,是北京华宇软件股份有限公司(股票简称:“华宇软件”,股票代码:300271)的全资子公 ...
- IE中window的模态框与返回值
window.returnValue是javascript中html的window对象的属性,目的是返回窗口值,当用window.showModalDialog函数打开一个IE的模态窗口时,用于返回窗 ...
- HDU 5288 OO’s Sequence
题意: 给你一个序列, 有一个函数 F(L,R) 其中 ai 均不能 被 aL - aR整除的 函数值是这个ai个数 思路 : 反过来求 满足这样的条件的 ai 的区间,然后求和 #include& ...
- SKlearn库学习曲线
思想: # 1.现将所有样本用交叉验证方法或者(随机抽样方法) 得到 K对 训练集-验证集# 2.依次对K个训练集,拿出数量不断增加的子集如m个,并在这些K*m个子集上训练模型.# 3.依次在对应训练 ...
- Day5-----------------------系统监控
1.top 命令 查看终端信息 who 显示终端用户有哪些 bash 开启终端进程 PID:进程身份证 buffer:缓冲区 cache:高速缓存 进程:动起来的文件,CPU调用运行的过程 2.fre ...
- JetBrains 授权服务器(License Server URLS)
分享几个已经部署好的在线验证服务器:http://idea.iteblog.com/key.php http://idea.imsxm.com/ http://103.207.69.64:1017 h ...
- MyCP(课下作业,必做)
作业要求 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyCP -tx XXX1.txt XXX2.bin 用来把文本文件(内容为 ...
- php计算给定时间之前的函数
这里给定一个时间,计算这个时间在多久前,比如:2天前,1年前 function prettyDate($date){ $time = strtotime($date); $now = time(); ...
- PHP 中解析 url 并得到 url 参数
这里介绍两种对url操作的方法: 1.拿到一个完整url后,如何解析该url得到里面的参数. /** * 解析url中参数信息,返回参数数组 */ function convertUrlQuery($ ...
- Java编程的逻辑 (12) - 函数调用的基本原理
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...