在上一篇文章中,我们总结并模拟了 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结点的更多相关文章

  1. React: 有状态组件生成真实DOM结点

    上次我们分析了无状态组件生成 DOM 的过程,无状态组件其实就是纯函数,它不维护内部的状态,只是根据外部输入,输出一份视图数据.而今天我们介绍的有状态组件,它有内部的状态,因此在组件的内部,可以自行对 ...

  2. React系列文章:无状态组件生成真实DOM结点

    在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({name ...

  3. React系列文章:JSX生成真实DOM结点

    在上一篇文章中,我们介绍了Babel是如何将JSX代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了Babel编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const style ...

  4. React: JSX生成真实DOM结点

    在上一篇文章中,我们介绍了 Babel 是如何将 JSX 代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了 Babel 编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const ...

  5. React 中的 Component、PureComponent、无状态组件 之间的比较

    React 中的 Component.PureComponent.无状态组件之间的比较 table th:first-of-type { width: 150px; } 组件类型 说明 React.c ...

  6. React中的高阶组件,无状态组件,PureComponent

    1. 高阶组件 React中的高阶组件是一个函数,不是一个组件. 函数的入参有一个React组件和一些参数,返回值是一个包装后的React组件.相当于将输入的React组件进行了一些增强.React的 ...

  7. react的redux无状态组件

    Provider功能主要为以下两点: 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件 接收Redux的store作为props,通过context对象传递给子孙组件上的connec ...

  8. 15. react UI组件和容器组件的拆分 及 无状态组件

    1.组件的拆分 组件拆分的前提 当所有的逻辑都出现在一个组件内时 组件会变得非常复杂 不便与代码的维护 所以对组件进行拆分 IU组件 进行页面渲染 容器组件  进行逻辑操作 UI组件的拆分 新建一个 ...

  9. Blazor中的无状态组件

    声明:本文将RenderFragment称之为组件DOM树或者是组件DOM节点,将*.razor称之为组件. 1. 什么是无状态组件 如果了解React,那就应该清楚,React中存在着一种组件,它只 ...

随机推荐

  1. JSON字符串转实体对象

    JSON转实体两种方式 代码片段 ; i < dt.Rows.Count; i++) { //Json字符串 string designJson = dt.Rows[i]["Desig ...

  2. Java学习-058-Jsoup爬虫获取中国所有的三级行政区划数据(三),处理二级编码缺失

    通过查看数据可知,直辖市或者某些三级行政区域没有对应的二级区域,为方便后续的地址使用,可自定义缺失的二级地址. 如下示例自定义的二级行政区域的名称为一级区域的名称,对应的源码如下所示: 将此段源码添加 ...

  3. Webpack 4 SplitChunksPlugin配置方案(转)

    通常情况下我们的 WebApp 是有我们的自身代码和第三方库组成的,我们自身的代码是会常常变动的,而第三方库除非有较大的版本升级,不然是不会变的,所以第三方库和我们的代码需要分开打包,我们可以给第三方 ...

  4. Linux CentOS7 下无图形界面安装Oracle11G R2版本

    01,系统 Centos7 数据库版本 Oracle_11gR2 ,以及硬件要求 内存不能小于 1G,可用硬盘不小于8G Swap分区空间不小于2G grep MemTotal /proc/memin ...

  5. canal使用

    报错信息:com.alibaba.druid.pool.DruidDataSource - testWhileIdle is true, validationQuery not set 解决方法: 找 ...

  6. 【LOJ511】[LibreOJ NOI Round #1]验题(动态DP)

    我这道题写了整!整!三!天! 我要一定要写这篇博客来表达我复!杂!的!心!情! 题目 LOJ511 官方题解(这个题解似乎不是很详细,我膜 std 才看懂的) 调这道题验证了我校某人的一句话:调题是一 ...

  7. Vue(九)使用Ant Design入门——环境搭建

    安装 npm install ant-design-vue --save 然后在main.js里面引入 import ant from 'ant-design-vue' import 'ant-des ...

  8. Highcharts 宽度溢出容器

    1,设置Highcharts的动态宽高. 获取Highcharts图表需要的宽高值,给到Highcharts图表的div容器. 如:var hpvCountSendDateHei = $(" ...

  9. 服务器BMC资料整理

    1. 现在服务器都有BMC管理了,可以直接连上服务器进行处理. bios里面进行简单设置就可以了, 连接上IPMI的口进行管理. 2. 可以使用 远程控制安装操作系统. 安装系统时 比较清楚的能够看到 ...

  10. Codeforces Round #604 (Div. 2) (题解)

    A. Beautiful String (暴力) 题目链接 题目大意: 给定一个字符串,只有 \(?a\ b\ c\ ?\) ,问是否存在一种将所有的 \(?\) 替换成 \(a\ b\ c\) ,使 ...