楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试的也都能答出来。但对于 react 只是有一定的了解,没有真实的学习过实现,虽然之前也看过一些文章,但是只停留在表面,因为别人这么写了,也就下意识的认为是这样。本次正好配合工作的契机,我们从零开始学习一下,使用的话呢就简单一过,相信大家也都用过或者看完官网也都了解了。如果您是大佬,欢迎批评指正;如果您是初级选手,希望能够一起学习。

初始化项目

我们借助脚手架实现开发环境,内部使用的库用自己开发的。

  1. npx create-react-app react-dome1 (当然也可以全局安装脚手架)
    public 目录只留下 index.htmlsrc 目录下只留下 index.js
  2. 修改 scripts 命令
    我们需要使用旧的转换方式,这样我们可以自己实现 createElement 方法
// 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"
},

react 17 引入了新的 jsx 转换特性,因为之前的开发,即使页面中未直接使用 React,但是也要引入,这是因为 babel 在转译之后会触发 React.createElement,如果不引入会报错,但是引入了可能也会触发 eslint 的报错,引入但是未使用。新特性可以单独使用 JSX 而无需引入 React

新特性一些好处

  1. 使用全新的转换,你可以单独使用 JSX 而无需引入 React
  2. 根据你的配置,JSX 的编译输出可能会略微改善 bundle 的大小。
  3. 它将减少你需要学习 React 概念的数量,以备未来之需

之前的转换方式

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' });
}

实现 React.createElement

我们先看下原生 createElement 的返回结果

// src/index.js
import React from 'react'
const jsx = <h1 className='title' style={{color: 'red'}}>hello</h1>
console.log(jsx)


我们看到返回了对象,几个重要属性,$$typeof, props, type。我们实现下自己的 createElement 函数。

定义类型常量

// src/constants.js// react 内的元素都是这个类型
export const REACT_ELEMENT = Symbol("react.element");
// react 文本类型
export const REACT_TEXT = Symbol("react.text");

实现 createElement

// src/react.js
// 这三个参数是 babel 解析完,调用React.createElement 传入的,从第三个参数开始都是儿子
function createElement(type, config, children) {if(config) {// 这里可写可不写,就是为了简化下我们自己写的,只把必要的返回,没用的参数越少越清晰嘛delete config.__sourcedelete 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

这里也可以 ...children 形式,判断只要判断 children 长度就可以了,但是属于 es6 的用法,我们按照源码实现

实现 toVdom 辅助函数

我们这里还要进行一下处理,因为如果是文本类型的话,直接就是字符串了,没有类型这种标识了,所以我们要对 children 进行一下包裹,也为了后面的 diff

// src/utils.js// 统一规范,方便  diff
export function toVdom(element) {return typeof element === "string" || typeof element === "number"? { // 字符串包裹$$typeof: REACT_ELEMENT,type: REACT_TEXT,props: element,}: element;
}

修改 createElement 函数,包裹儿子节点

...
props.children = Array.prototype.slice.call(arguments, 2).map(toVdom);
...
props.children = toVdom(children);

调用我们自己的实现,我们可以得到如下结果

页面挂载

我们引入 react-dom,看下原生渲染

import React from "react";
import ReactDOM from "react-dom";let jsx = (<h1 className="title" style={{ color: "red", backgroundColor: "pink" }}>hello<span>111</span></h1>
);
ReactDOM.render(jsx, document.getElementById("root"));

实现 reactDOM.render

大家可以按我写的第几步阅读,基本都做了注视

// 做了两件事
// 1. 虚拟dom变真实dom
// 2. 挂载
function render(vdom, container) {            //。 第二步//1 const newDOM = createDOM(vdom) // 不同功能写在不同函数里,清晰          // 第三步//2container.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)         // 第四步// 单独处理 chidlrenif(props.chidlren && typeof props.children === 'object' && props.chidlren.$$typeof) {// 文本render(props.chidlren, dom)} else if (Array.isArray(props.children)) {// 子为数组,把子挂载到当前的父 domreconcileChildren(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";


可以看到,也实现了渲染

本篇就介绍到这里,我们了解了虚拟 dom 的对象形式,了解了如果挂载到页面上,下一节我们学习下类组件和函数组件的实现,如果有不对,欢迎指正!

react 学习(一) 实现简版虚拟 dom 和挂载相关推荐

  1. 初识react(二) 实现一个简版的html+redux.js的demo

    回顾 初识react(一) 揭开jsx语法和虚拟DOM面纱 初识react(二) 实现一个简版的html+redux.js的demo 初识react(三)在 react中使用redux来实现简版计数器 ...

  2. 手把手教你React(一)JSX与虚拟DOM

    初衷 学习React有一段时间了, 一直想找个时间写一个React的系列文章.忙里抽闲,完成了第一篇.写这系列文章的初衷是总结这段时间的技术学习,以及给那些想学习React的同学们一点帮助.我会尽量以 ...

  3. 深入理解React(一)JSX与虚拟DOM

    初衷 使用 React 有一段时间了, 一直想找个时间写一个 React 的系列文章.忙里抽闲,完成了第一篇.写这系列文章的初衷是总结这段时间的技术学习,以及给那些想学习 React 的同学们一点帮助 ...

  4. React 入门与实战-课时7 虚拟DOM的本质和目的

    DOM树的概念: 一个网页呈现的过程: 1.浏览器请求服务器获取页面HTML代码 2.浏览器先在内存中,解析DOM结构,并在浏览器内存中,渲染出一颗DOM树: 3.浏览器把DOM树,呈现到页面上: R ...

  5. 【React】通过jsx语法创建虚拟DOM动态及数组赋值的方法(图文+代码)

    jsx语法规则: 1.定义虚拟DOM时,不要写引号. 2.标签中混入JS表达式时要用{}. 3.样式的类名指定不要用class,要用className. 4.内联样式,要用style={{key:va ...

  6. 从 React 历史的长河里聊虚拟 DOM 及其价值

    什么是虚拟 DOM 本质上是 JavaScript 对象,这个对象就是更加轻量级的对 DOM 的描述. 对,就是这么简单! 就是一个复杂一点的对象而已,没什么好说的,重点是为什么要有这个东西,以及有了 ...

  7. 深度学习实战16(进阶版)-虚拟截图识别文字-可以做纸质合同和表格识别

    大家好,我是微学AI,今天给大家带来一个关于虚拟截图识别文字的应用,可以运用在多个领域. 案例主要结合Mediapipe手势识别模型,识别出手势的21个关键点的坐标,计算机的摄像头根据食指的坐标信息获 ...

  8. 前端学习(3196):虚拟dom和真实dom

  9. 前端学习(3195):虚拟dom的创建方式1的js

最新文章

  1. 60颗卫星被五手火箭送上天!马斯克疯狂的卫星互联网计划不远了
  2. 计算机也可以看“视频”,理解“视频”
  3. 64 大小_电脑系统32位和64位有什么区别
  4. Spring 3整合Quartz 2实现定时任务--转
  5. JS 枚举型变量操作
  6. 来,和腾讯一起共建未来城市
  7. java epoll select_最新阿里、拼多多、快手Java岗面试题269 道送答案
  8. php购物车内物品删除,求助 购物车 用session删除 列表的一条
  9. extjs xtype 类型
  10. linux系统外接硬盘_如何使用外部硬盘安装linux系统?
  11. 连接游戏服务器网络延迟高,玩游戏网络延迟高怎么办 网络卡Ping值很高的解决方法...
  12. “九个字、一只手、专有云”,有孚网络的云上之路
  13. 网络打印协议之LPR或RAW
  14. 微信开发工具button跳转页面_微信小程序按钮点击跳转页面详解
  15. android百度地图路线查询,Android百度地图——路线规划搜索
  16. php代码托管平台,程序员必须知道的几个Git代码托管平台
  17. boss是董卓的java游戏_武将列传世界BOSS董卓-殒命长安攻略
  18. s2系列——s2-012,s2-013,s2-015,s2-016
  19. python流量监控脚本
  20. 我的小情绪、小失落、小想念

热门文章

  1. Python自动化应用案例:一键生成工厂物料采购订单(精益办公案例之三)
  2. Python 作业一
  3. 计算机软件创新,利川计算机软件学院_创新学校
  4. 美创科技OSM助力长安大学迈上信息化新台阶
  5. AWS-S3通用存储操作,操作minio、oss、cos等所有兼容s3协议的云存储(含有大文件分片上传实现)
  6. echarts中的map地图的使用
  7. 【acwing】1018. 最低通行费*
  8. 宠喵club商品页皇家商品
  9. C# 调用CodeSoft 方法
  10. JSP学习笔记(动力节点老杨)(自己总结方便复习)