Open buxuku opened 3 years ago
因为从React 17开始,不再使用React环境下的createElement了,介绍全新的 JSX 转换. 所以我们先禁用这一特性.
createElement
yarn add cross-env
然后将package.json里面的start命令修改为:
start
"start": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts start",
我们接触React最首先就会接触到JSX,JSX实际上就是React里面的一个语法糖,因为我们知道HTML片段放在js里面本身它是没办法识别并运行的. 它需要借助babel转换为js脚本.
打开babel,输入下面这一块HTML片段
<h1 id="title">hello <span>world</span></h1>
可以看到babel将它转换成了可执行的js代码
"use strict"; /*#__PURE__*/ React.createElement("h1", { id: "title" }, "hello ", /*#__PURE__*/React.createElement("span", null, "world"));
它这里就是调用了React里面的createElement这个API.
咱们都知道,React处理的是虚拟DOM, 所以React.createElement生成出来的是一个虚拟DOM,而不像Document.createElement生成出来的是一个真实的DOM节点.
React.createElement
Document.createElement
那么这个虚拟DOM长什么样子呢,咱们在index.js文件里面打印一下这个element组件看看.
index.js
element
import React from 'react'; import ReactDOM from 'react-dom'; const element = <h1>hello <span>world!</span></h1>; + console.log('element', element); ReactDOM.render( element, document.getElementById('root') );
我们看到控制台输出了:
它其实就是一个对象,其中除了_source,_self,_store等这些由babel自动生成的值以外,我们看到这个虚拟DOM其实就是一个js对象,里面包含了咱们常见的key,ref,props,children.以及$$typeof这个字段来表示这是一个React组件,type代表了组件的类型.
_source
_self
_store
key
ref
props
children
$$typeof
type
从刚刚上面babel转换的结果可以看到React.createElement这个函数接收的参数,第一个参数就是type代表组件类型,第二个参数就是组件的配置集合,包括组件的props,ref, attribute属性等, 后面的所有参数就是该组件的子元素.有多少个子元素,就有多少个参数,所以后面的参数个数是不确定的.同时我们也发现了,children这个属性是挂在props上面的,并且父组件如果有多个子元素,那么这个children将会是一个数组.
attribute
好了, 虚拟DOM大概就是长这个样子,那么我们就可以实现一个React.createElement方法来返回这个虚拟DOM即可.
在src/react/index.js新建一个文件,实现咱们第一个API,这里我们先不处理ref,key这些属性.
src/react/index.js
/** * 生成虚拟DOM * @param type * @param props * @param children * @returns {{ref: null, $$typeof: symbol, text: null, type, key: null, props: {}}} */ const createElement = (type, config = {}, ...children) => { const props = {...config}; if (children.length) { props.children = children.length > 1 ? children : children[0]; } return { $$typeof: Symbol.for('react.element'), type, props, key: null, ref: null, } } const React = { createElement } export default React;
在src/index.js使用我们自己写的React
src/index.js
-import React from 'react'; +import React from './react';
试试看效果,居然能正常运行,这似乎是不是有点太简单了?
既然这个虚拟DOM是一个js对象,那么我们是不是也可以直接对这个虚拟DOM进行任意的增删改查呢?我们用官方的React,对element这个对象执行一下修改操作
element.type = 'p';
控制台报错了
TypeError: Cannot assign to read only property 'type' of object '#<Object>'
我们尝试增加一个属性
element.test = 'test';
控制台依然报错了
TypeError: Cannot add property test, object is not extensible
可以看到这个对象是被Object.freeze()处理过的,并且还是深遍历处理了的.当然,目前我们还是先不考虑这一步,只有先明白原生的虚拟DOM这样一个特性.
Object.freeze()
接下来咱们继续2.ReactDOM.render
2.ReactDOM.render
因为从React 17开始,不再使用React环境下的
createElement
了,介绍全新的 JSX 转换. 所以我们先禁用这一特性.然后将package.json里面的
start
命令修改为:我们接触React最首先就会接触到JSX,JSX实际上就是React里面的一个语法糖,因为我们知道HTML片段放在js里面本身它是没办法识别并运行的. 它需要借助babel转换为js脚本.
打开babel,输入下面这一块HTML片段
可以看到babel将它转换成了可执行的js代码
它这里就是调用了React里面的
createElement
这个API.咱们都知道,React处理的是虚拟DOM, 所以
React.createElement
生成出来的是一个虚拟DOM,而不像Document.createElement
生成出来的是一个真实的DOM节点.那么这个虚拟DOM长什么样子呢,咱们在
index.js
文件里面打印一下这个element
组件看看.我们看到控制台输出了:
它其实就是一个对象,其中除了
_source
,_self
,_store
等这些由babel自动生成的值以外,我们看到这个虚拟DOM其实就是一个js对象,里面包含了咱们常见的key
,ref
,props
,children
.以及$$typeof
这个字段来表示这是一个React组件,type
代表了组件的类型.从刚刚上面babel转换的结果可以看到
React.createElement
这个函数接收的参数,第一个参数就是type
代表组件类型,第二个参数就是组件的配置集合,包括组件的props
,ref
,attribute
属性等, 后面的所有参数就是该组件的子元素.有多少个子元素,就有多少个参数,所以后面的参数个数是不确定的.同时我们也发现了,children
这个属性是挂在props
上面的,并且父组件如果有多个子元素,那么这个children
将会是一个数组.好了, 虚拟DOM大概就是长这个样子,那么我们就可以实现一个
React.createElement
方法来返回这个虚拟DOM即可.在
src/react/index.js
新建一个文件,实现咱们第一个API,这里我们先不处理ref
,key
这些属性.在
src/index.js
使用我们自己写的React试试看效果,居然能正常运行,这似乎是不是有点太简单了?
既然这个虚拟DOM是一个js对象,那么我们是不是也可以直接对这个虚拟DOM进行任意的增删改查呢?我们用官方的React,对element这个对象执行一下修改操作
控制台报错了
我们尝试增加一个属性
控制台依然报错了
可以看到这个对象是被
Object.freeze()
处理过的,并且还是深遍历处理了的.当然,目前我们还是先不考虑这一步,只有先明白原生的虚拟DOM这样一个特性.接下来咱们继续
2.ReactDOM.render