博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React系列:无状态组件生成真实DOM结点
阅读量:5362 次
发布时间:2019-06-15

本文共 4289 字,大约阅读时间需要 14 分钟。

在上一篇文章中,我们总结并模拟了 JSX 生成真实 DOM 结点的过程,今天接着来介绍一下无状态组件的生成过程。

先以下面一段简单的代码举例:

const Greeting = function ({name}) {  return 
{`hello ${name}`}
;};const App =
;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 结构:

317749-20180921132513020-959972589.png

我们再来看一个稍微复杂些的例子:

const Greeting = function ({name}) {  return (    
{`hello ${name}`}
);};const Container = function ({children}) { return (
{children}
);};const App = (
);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 结构:

317749-20180921181844390-922502485.png

最后,再来看看 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 结构:

317749-20180921182257218-1774386357.png

转载于:https://www.cnblogs.com/liuhe688/p/10970784.html

你可能感兴趣的文章
Code Snippet
查看>>
zoj 1232 Adventure of Super Mario
查看>>
组合数学 UVa 11538 Chess Queen
查看>>
Redis常用命令
查看>>
[转载]电脑小绝技
查看>>
thinkphp如何实现伪静态
查看>>
BZOJ 1925: [Sdoi2010]地精部落( dp )
查看>>
c++中的string常用函数用法总结!
查看>>
Week03-面向对象入门
查看>>
一个控制台程序,模拟机器人对话
查看>>
Vue 2.x + Webpack 3.x + Nodejs 多页面项目框架(上篇——纯前端多页面)
查看>>
我的PHP学习之路
查看>>
【题解】luogu p2340 奶牛会展
查看>>
解决响应式布局下兼容性的问题
查看>>
使用DBCP连接池对连接进行管理
查看>>
【洛谷】【堆+模拟】P2278 操作系统
查看>>
hdu3307 欧拉函数
查看>>
Spring Bean InitializingBean和DisposableBean实例
查看>>
[容斥][dp][快速幂] Jzoj P5862 孤独
查看>>
软件开发工作模型
查看>>