学习建议:下载本节代码,对照着文章查看,尽量动手实现一遍。
1、前言
在React17之前,我们写React代码的时候都会去引入React,不引入代码就会报错,而且自己的代码中没有用到React,这是为什么呢?带着这个问题我们向下学习;
import React from 'react'
2、element变量解析
我们先创建一个element变量,将本段代码放到babel上查看编译结果:
const element = <h1 title="foo">Hello</h1>
通过babel会编译成下面这种形式:
经过编译后的代码为:
const element = React.createElement("div", {title: "foo"}, "Hello");
element参数说明:
- dom元素
- 属性
- children子元素
解答一下开篇提出的问题:引入React的作用,使用React进行解析JSX,如果不引入React,上面代码就会报错。JSX实际上是一个语法糖,它真正是需要解析成js代码来执行;
3、创建项目
我们先来创建执行命令:
npm init
安装相关的依赖:
npm install --save-dev babel-loader @babel/corenpm install webpack --save-devnpm install --save-dev @babel/preset-reactnpm install --save-dev html-webpack-plugin
创建项目目录:
配置webpack:
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',}),],}
加入启动命令:
4、打印结果值
创建一个真实的React项目,使用create-react-app,本文就不在叙述安装过程。再来看看上文的 React.createElement 实际生成了的是什么?打印一下element:
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)
打印结果:
简化一下,将其他属性刨除(其他属性我们不关心):
const element = {type: "h1",props: {title: "foo",children: "Hello",},}
简单总结一下,React.createElement 实际上是生成了一个 element 对象,包含两个属性对象 type 和 props ,该对象拥有以下属性:
element对象参数:
- type:标签名称
- props:属性
- title:标签属性
- children:子属性
5、render简单流程
提前了解一下render的简单流程:
ReactDOM.render() 将 element 添加到 id 为 root 的 DOM 节点中,我们接下来实现这个方法来代替React源码中的 ReactDOM.render()方法;
示例代码:
const element = {type: "h1",props: {title: "foo",children: "Hello",},}
1.首先,我们使用元素类型创建一个节点(element.type) ,在本例中是 h1;
const node = document.createElement(element.type)
2.设置节点属性为title;
node["title"] = element.props.title
3.只有一个字符串作为子节点,我们创建一个文本节点,并且设置文本节点的nodeValue为element.props.children;
const text = document.createTextNode("")text["nodeValue"] = element.props.children
4.最后,我们将 textNode 附加到 h1,并将 h1附加到容器;
node.appendChild(text)container.appendChild(node)
6、createElement实现(虚拟DOM)
用我们自己的代码实现React的代码;
从上文了解到createElement的作用是创建一个element对象:
const element = {type: "h1", //标签props: {title: "foo", // 属性children: "Hello", // 节点},}
调用方式:
const element = React.createElement("div", {title: "foo"}, "Hello");
根据调用和返回结果,设计createElement函数如下:
// 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结构:
const element = (<div id="foo"><a>bar</a><b /></div>)
测试一下:
// src/index.jsimport React from '../react';const element = (<div id="foo"><a>bar</a><b /></div>)console.log(element
打印结果:
7、本节代码
地址:https://github.com/linhexs/mini-react/tree/1.createElement
