前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >跟着写一遍就会了,手写一个mini版本的React(1.createElement)

跟着写一遍就会了,手写一个mini版本的React(1.createElement)

作者头像
玖柒的小窝
修改2021-10-20 10:16:02
9790
修改2021-10-20 10:16:02
举报
文章被收录于专栏:各类技术文章~各类技术文章~

1、前言:

本文通过手写一个简易版 React,对 React 基本原理有一个直观的认识;

实现的版本为16.8,基于pomb.us/build-your-…

学习前提需要有React基础;

2、实现目标

  1. createElement;
  2. render;
  3. 并发模式;
  4. Fibers;
  5. 渲染和提交 ;
  6. 协调;
  7. 函数组件;
  8. hooks;
  9. 类组件

学习建议:下载本节代码,对照着文章查看,尽量动手实现一遍。

3、createElement实现

3.1 思考

在React17之前,我们写React代码的时候都会去引入React,不引入代码就会报错,而且自己的代码中没有用到React,这是为什么呢?带着这个问题我们向下学习;

代码语言:javascript
复制
import React from 'react'
复制代码

3.2 element变量解析

我们先创建一个element变量,将本段代码放到babel上查看编译结果:

代码语言:javascript
复制
const element = <h1 title="foo">Hello</h1>
复制代码

通过babel会编译成下面这种形式:

image.png
image.png

经过编译后的代码为:

代码语言:javascript
复制
const element = React.createElement("div", {
  title: "foo"
}, "Hello");
复制代码

element参数说明:

  • dom元素
  • 属性
  • children子元素

解答一下开篇提出的问题:引入React的作用,使用React进行解析JSX,如果不引入React,上面代码就会报错。JSX实际上是一个语法糖,它真正是需要解析成js代码来执行;

3.3 创建项目

我们先来创建执行命令:

代码语言:javascript
复制
npm init 
复制代码

安装相关的依赖:

代码语言:javascript
复制
npm install --save-dev babel-loader @babel/core
npm install webpack --save-dev
npm install --save-dev @babel/preset-react
npm install --save-dev html-webpack-plugin 
复制代码

创建项目目录:

image.png
image.png

配置webpack:

代码语言:javascript
复制
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    main: './src/index.js'
  },
  devServer: {
    port: 9000,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-transform-react-jsx']
          }
        }
      }
    ]
  },
  mode: "development",
  optimization: {
    minimize: false
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'React',
    }),
  ],
}
复制代码

加入启动命令:

image.png
image.png

3.4 打印结果值

创建一个真实的React项目,使用create-react-app,本文就不在叙述安装过程。再来看看上文的 React.createElement 实际生成了的是什么?打印一下element:

代码语言:javascript
复制
import React from 'react';
import ReactDOM from 'react-dom';

const element = <h1 title="foo">Hello</h1>
console.log(element)

const container = document.getElementById("root")
ReactDOM.render(element, container)
复制代码

打印结果:

image.png
image.png

简化一下,将其他属性刨除(其他属性我们不关心):

代码语言:javascript
复制
const element = {
  type: "h1",
  props: {
    title: "foo",
    children: "Hello",
  },
}
复制代码

简单总结一下,React.createElement 实际上是生成了一个 element 对象,包含两个属性对象 type 和 props ,该对象拥有以下属性:

element对象参数:

  • type:标签名称
  • props:属性
    • title:标签属性
    • children:子属性

3.5 render简单流程

提前了解一下render的简单流程:

image.png
image.png

ReactDOM.render() 将 element 添加到 id 为 root 的 DOM 节点中,我们接下来实现这个方法来代替React源码中的 ReactDOM.render()方法; ​

示例代码:

代码语言:javascript
复制
const element = {
  type: "h1",
  props: {
    title: "foo",
    children: "Hello",
  },
}
复制代码

1.首先,我们使用元素类型创建一个节点(element.type) ,在本例中是 h1;

代码语言:javascript
复制
const node = document.createElement(element.type)
复制代码

2.设置节点属性为title;

代码语言:javascript
复制
node["title"] = element.props.title
复制代码

3.只有一个字符串作为子节点,我们创建一个文本节点,并且设置文本节点的nodeValue为element.props.children;

代码语言:javascript
复制
const text = document.createTextNode("")
text["nodeValue"] = element.props.children
复制代码

4.最后,我们将 textNode 附加到 h1,并将 h1附加到容器;

代码语言:javascript
复制
node.appendChild(text)
container.appendChild(node)
复制代码

3.6 createElement实现(虚拟DOM)

用我们自己的代码实现React的代码; ​

从上文了解到createElement的作用是创建一个element对象:

代码语言:javascript
复制
const element = {
  type: "h1",  //标签
  props: {
    title: "foo", // 属性
    children: "Hello", // 节点
  },
}
复制代码

调用方式:

代码语言:javascript
复制
const element = React.createElement("div", {
  title: "foo"
}, "Hello");
复制代码

根据调用和返回结果,设计createElement函数如下:

代码语言:javascript
复制
// react/createElement.js

/**
 * 创建虚拟DOM结构
 * @param {*} type 标签
 * @param {*} props 属性
 * @param  {...any} children 自己诶单 
 * @returns 虚拟DOM结构
 */
export function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map(child =>
        typeof child === "object" 
          ? child  
          : createTextElement(child) //不是对象说明是文本节点
      ),
    },
  }
}


/**
 * 当children为非对象时,创建文本节点
 * @param {*} text 文本值
 * @returns 虚拟DOM结构
 */
function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: [],
    },
  }
}
复制代码

为了直观的展示,我们更改一下element结构:

代码语言:javascript
复制
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
)
复制代码

测试一下:

代码语言:javascript
复制
// src/index.js
import React from '../react';

const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
)

console.log(element
复制代码

打印结果:

image.png
image.png

3.7 本节代码

地址:gitee.com/linhexs/rea…

4、参考链接

  1. pomb.us/build-your-…
  2. react.iamkasong.com/

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、前言:
  • 2、实现目标
  • 3、createElement实现
    • 3.1 思考
      • 3.2 element变量解析
        • 3.3 创建项目
          • 3.4 打印结果值
            • 3.5 render简单流程
              • 3.6 createElement实现(虚拟DOM)
                • 3.7 本节代码
                • 4、参考链接
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档