create-react-app
- 脚手架需要node14及以上版本
- npm i create-react-app -g
- create-react-app 项目名
createElement
- jsx是一种格式,表现为函数返回dom
- 我们日常会使用jsx来写react代码,但是react本身是无法识别jsx的,所以会用babel将jsx转换为React.createElement,然后react在浏览器里面跑的时候会执行React.createElement函数,返回react元素
<h1 className="title" style={{ color: "red" }}>哈哈</h1>
React.createElement('h1',
{
className: 'title',
style: {color: 'red'}
},'哈哈')
- 在浏览器中的React元素
- 实际就是React.createElement函数的执行结果
createElement有新老两版
- 老版本:写项目的时候,通常会发现,明明当前文件没用到React,但不引入React却会报错
- 因为它编译完为React.createElement,用到了React的createElement方法,那么没有React就会导致方法找不到
- 开启老版本的方法:
npm i cross-env --save
- 在start原本命令前里添加
cross-env DISABLE_NEW_JSX_TRANSFORM=true
React.createElemet('h1', null, '哈哈')
- 新版本:内部自动引入jsx包,转换方法为jsx,用法一致,好处是不用哪里都引入React了
- 17以后就是新版本
import jsx from 'jsx';
jsx('h1', null, '哈哈')
模拟一下react数据结构
export const REACT_ELEMENT = Symbol("react.element");
import { REACT_ELEMENT } from "./element";
function createElement(type, config, children) {
let ref;
let key;
if (config) {
delete config.__source;
delete config.__self;
ref = config.ref;
key = config.key;
delete config.ref;
delete config.key;
}
let props = { ...config };
if (arguments.length > 3) {
props.children = Array.prototype.slice.call(arguments, 2);
} else {
props.children = children;
}
return {
$$typeof: REACT_ELEMENT,
type,
ref,
key,
props,
};
}
const React = {
createElement,
};
export default React;
- 现在返回大致是一样的了,但是我们做domDiff的时候还得判断当前的dom是字符串或数字或对象,有点麻烦,我们直接把字符串和数组转为对象(源码里没有这样处理,这里只是省事儿)
export const REACT_TEXT = Symbol("REACT_TEXT");
export function wrapToVdom(element) {
return typeof element === "string" || typeof element === "number"
? { type: REACT_TEXT, props: element }
: element;
}
....
if (arguments.length > 3) {
props.children = Array.prototype.slice.call(arguments, 2).map(wrapToVdom);
} else {
props.children = wrapToVdom(children);
}
....
ReactDom简单实现
render
import { REACT_TEXT } from "./utils";
function render(vdom, container) {
mount(vdom, container);
}
function mount(vdom, container) {
let newDom = createDom(vdom);
container.appendChild(newDom);
}
function createDom(vdom) {
let { type, props } = vdom;
let dom;
if (type === REACT_TEXT) {
dom = document.createTextNode(props);
} else {
dom = document.createElement(type);
}
if (props) {
updateProps(dom, {}, props);
if (typeof props.children === "object" && props.children.type) {
mount(props.children, dom);
} else if (Array.isArray(props.children)) {
reconcileChildren(props.children, dom);
}
}
return dom;
}
function reconcileChildren(children, prarentDom) {
for (let i = 0; i < children.length; i++) {
mount(children[i], prarentDom);
}
}
function updateProps(dom, oldProps = {}, newProps = {}) {
for (let key in newProps) {
if (key === "children") {
continue;
} else if (key === "style") {
let styleObj = newProps["style"];
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr];
}
} else {
dom[key] = newProps[key];
}
}
for (let key in oldProps) {
if (!newProps.hasOwnProperty(key)) {
oldProps[key] = null;
}
}
}
const ReactDom = { render };
export default ReactDom;
使用我们的react与react-dom
import React from "./react";
import ReactDOM from "./react-dom";
let element = (
<h1
className="title"
style={{ color: "red" }}
>
哈哈
<span>我是span</span>
</h1>
);
ReactDOM.render(element, document.getElementById("root"));