|
2019-05-17
// cross-env 需要自己安装
scripts": {
"start": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts start",
"build": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts build",
"test": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts test",
"eject": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts eject"
},
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
====================================
import React from 'react';
function App() {
return React.createElement('h1', null, 'Hello world');
}
function App() {
return <h1>Hello World</h1>;
}
==================================
// 由编译器引入(禁止自己引入!)
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
// src/index.js
import React from 'react'
const jsx = <h1 className='title' style={{color: 'red'}}>hello</h1>
console.log(jsx)
// src/constants.js
// react 内的元素都是这个类型
export const REACT_ELEMENT = Symbol("react.element");
// react 文本类型
export const REACT_TEXT = Symbol("react.text");
// src/react.js
// 这三个参数是 babel 解析完,调用React.createElement 传入的,从第三个参数开始都是儿子
function createElement(type, config, children) {
if(config) {
// 这里可写可不写,就是为了简化下我们自己写的,只把必要的返回,没用的参数越少越清晰嘛
delete config.__source
delete config.__self
}
const props = {...config}
if (arguments.length > 3) {
// 有多个儿子
props.chidlren = Array.prototype.slice.call(arguments, 2)
} else if (argument.length === 3) {
// 只有一个子,直接赋值
props.children = children
}
return {
$$typeof: REACT_ELEMENT,
type,
props
}
}
const React = {
createElement
}
export default React
// src/utils.js
// 统一规范,方便 diff
export function toVdom(element) {
return typeof element === "string" || typeof element === "number"
? { // 字符串包裹
$$typeof: REACT_ELEMENT,
type: REACT_TEXT,
props: element,
}
: element;
}
...
props.children = Array.prototype.slice.call(arguments, 2).map(toVdom);
...
props.children = toVdom(children);
调用我们自己的实现,我们可以得到如下结果
// 做了两件事
// 1. 虚拟dom变真实dom
// 2. 挂载
function render(vdom, container) { //。 第二步
//1
const newDOM = createDOM(vdom) // 不同功能写在不同函数里,清晰 // 第三步
//2
container.appendChild(newDOM)
}
// 创建真实 dom
function createDOM(vdom) {
let {type, props} = vdom // 我们知道虚拟dom就是我们生成的那个对象
let dom // 最后要返回的
if (type === REACT_TEXT) {
// 如果是个文本
dom = document.createTextNode(props)
} else {
// 标签节点
dom = document.createElement(type)
}
// 需要对props 中的 style 和 children 和其他进行处理
if(props) {
// 单独处理属性
updateProps(dom, {}, props) // 第四步
// 单独处理 chidlren
if(props.chidlren && typeof props.children === 'object' && props.chidlren.$$typeof) {
// 文本
render(props.chidlren, dom)
} else if (Array.isArray(props.children)) {
// 子为数组,把子挂载到当前的父 dom
reconcileChildren(props.children, dom) // 第五步
}
}
return dom
}
// 子虚拟节点,父真实节点
function reconcileChildren(chidlrenVdom, parentDom) {
// 循环递归处理, 算法题里非二叉树的多子树节点,也是 for 循环遍历处理
for (let i = 0; i < childrenVdom.length; i++) {
render(childrenVdom[i], parentDOM);
}
}
// 对 dom 进行新属性赋值,旧属性没有的删除, vue中也是类型的操作,遍历新属性,旧属性
function updateProps(dom, oldProps, newProps) {
for(let key in newProps) {
if (key === 'children') {
continue // 单独处理
} else if (key === 'style') {
let styleObj = newProps[key]
for(let attr in styleObj) {
dom.style[attr] = styleObj[attr]
}
} else {
dom[key] = newProps[key]
}
}
// 老的有,新的没有 删除
for(let const key in oldProps) {
if (!newProps.hasOwnProperty(key)) {
delete dom[key]
}
}
}
// 根据调用,返回的一定是对象 第一步
const ReactDOM = {
render
}
export default ReactDOm
// src/index.js
import React from "./react";
import ReactDOM from "./react-dom";
编辑:航网科技 来源:腾讯云 本文版权归原作者所有 转载请注明出处
微信扫一扫咨询客服
全国免费服务热线
0755-36300002