react-2

用jsx写的代码,babel会将其转化为react的虚拟dom,如下图:

image.png

可以看到,转化后,是用React.createElement方法来生成dom的,它大约有3(更多)个参数: type,props, childrenprops是这个元素的详情,children可以有多个,代表这个元素的子元素, 那么它的执行结果又是什么呢?
image.png

上图打印出的对象就是react元素,他是一个类似真实dom的对象,type是标签名,props是其属性,像attribute,class,id等都存在于此,props中还有一个比较特殊的属性children,它是子元素集合(只有一个子元素(即文本元素)时,它的类型为string)。
接下来,我们就来写一个简易版的react+react-dom

// 首先,完成react
function createElement(type, config, ...children) {
  // 将children使用rest参数的方法合并起来
  let props = { ...config, children };
  return { type, props };  // 此时,就生成虚拟元素
}

export default {
  createElement
};

接下来是重头戏,react-dom

function render(element, parent) {
  if (typeof element === "string") {
    //文本元素,直接生成,随后插入父元素
    element = document.createTextNode(element);
  }
  let { type, props } = element; //element是一个虚拟dom对象
  if (typeof type === "string") {
    element = document.createElement(type);
    for (let propName in props) {
      if (propName === "style") {  // 添加样式
        Object.entries(props.style).forEach(([attr, value]) => {
          element.style[attr] = value;
        });
      } else if (propName === "className") {  // 添加class
        element.className = props.className;
      } else if (propName === "children") { 
        props.children.forEach(child => {  // 生成child并且插入父级
          return render(child, element);
        });
      }
    }
  }
  parent.appendChild(element);
}

export default {
  render
};

业务代码:

//index.js
...
let elm = React.createElement(
  "h1",
  { className: "title", style: { backgroundColor: "green" } },
  "hello",
  React.createElement("span", null, "world")
);
ReactDOM.render(elm, document.getElementById("root"));

image.png

正常输出了!
以上,只是演示了普通的html元素,接下来,再兼容函数组件类组件

//index.js
function Welcome(props) { //函数式组件
  return React.createElement(
    "h1",
    { className: "title", style: { backgroundColor: "green" } },
    props.name,
    React.createElement("span", null, props.age)
  );
}
// class Welcome extends React.Component {   //类组件
//   render() {
//     return React.createElement(
//       "h1",
//       { className: "title", style: { backgroundColor: "green" } },
//       this.props.name,
//       React.createElement("span", null, this.props.age)
//     );
//   }
// }
let element = React.createElement(Welcome, { name: "lc", age: 18 });
ReactDOM.render(element, document.getElementById("root"));

修改react/index.js,以支持类组件

function createElement(type, config, ...children) {
  let props = { ...config, children };
  let element = { type, props };
  return element;
}

class Component {
  static isReactComponent = true;
  constructor(props) {
    this.props = props;
  }
}

export default {
  createElement,
  Component,
};

再来修改react-dom,以支持解析函数式和类组件

//  react-dom/index.js
function render(element, parent) {
  let { type, props } = element; //element是一个虚拟dom对象
  if (typeof element === "string" || typeof element === "number") { //文本元素
    //文本元素,直接生成,随后插入父元素
    element = document.createTextNode(element);
  } else if (type.isReactComponent) { 
    //类组件
    element = new type(props).render();
    type = element.type;
    props = element.props;
  } else if (typeof type === "function") {  // 函数组件
    element = type(props);
    type = element.type;
    props = element.props;
  }
  if (typeof type === "string") {  // 
    element = document.createElement(type);
    for (let propName in props) {
      if (propName === "style") {
        Object.entries(props.style).forEach(([attr, value]) => {
          element.style[attr] = value;
        });
      } else if (propName === "className") {
        element.className = props.className;
      } else if (propName === "children") {
        props.children.forEach(child => {
          return render(child, element);
        });
      }else{
          dom.setAttribute(propName, props[propName])
      }
    }
  }
  parent.appendChild(element);
}

export default {
  render
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355