首页 > 其他分享 >React核心工作原理

React核心工作原理

时间:2022-09-28 08:45:42浏览次数:74  
标签:node 核心 vnode React props 组件 原理 JSX

## 1.1、虚拟DOM

常见问题:react virtual dom是什么?说一下diff算法?

拿到一个问题,一般回答都是是什么?为什么?怎么办?那就按照这个思路来吧!

what

用 JavaScript 对象表示 DOM 信息和结构,当状态变更的时候,重新渲染这个 JavaScript 的对象结构。这个 JavaScript 对象称为virtual dom;

why

DOM操作很慢,轻微的操作都可能导致页面重新排版,非常耗性能。相对于DOM对象,js对象处理起来更快,而且更简单。通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行dom操作,从而提高性能。

where

React中用JSX语法描述视图,通过babel-loader转译后它们变为React.createElement(...)形式,该函数将生成vdom来描述真实dom。将来如果状态变化,vdom将作出相应变化,再通过diff算法对比新老vdom区别从而做出最终dom操作。

这里说到了JSX,那就顺带大致说一下:

什么是JSX

语法糖, React 使用 JSX 来替代常规的 JavaScript。

JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

React实战视频讲解:进入学习

为什么需要JSX

开发效率:使用 JSX 编写模板简单快速。

执行效率:JSX编译为 JavaScript 代码后进行了优化,执行更快。

类型安全:在编译过程中就能发现错误。

React 16原理

babel-loader会预编译JSX为React.createElement(...)

React 17原理

React 17中的 JSX 转换不会将 JSX 转换为 React.createElement,而是自动从 React 的 package 中引入新的入口函数并调用。另外此次升级不会改变 JSX 语法,旧的 JSX 转换也将继续工作。

与vue的异同

react中虚拟dom+jsx的设计一开始就有,vue则是演进过程中才出现的,2.0版本后出现。

jsx本来就是js扩展,转义过程简单直接的多;vue把template编译为render函数的过程需要复杂的编译器转换字符串-ast-js函数字符串

1.2、render、Component基础核心api

render

ReactDOM.render(element, container[, callback]);

当首次调用的时候,容器节点里的所有DOM 元素都会被替换,后续的调用则会使用React的DOM的差分算法(DOM diffing algorithm)进行高效的更新。

如果提供了可选的回调函数,该回调将在组件被渲染或更新之后被执行。

节点类型

1、文本节点
2、html 标签节点
3、函数组件
4、类组件
...

函数组件

// 大些字母开头
function Welcome(props) {
    return <h1>Hello, {props.name}</h1>
}

类组件

React 的组件可以定义为class 或函数的形式,如需定义class 组件,需要继承React.Component 或 React.PureComponent:

class Welcome extends React.Component {
    render() {
        return <h1>Hello, {this.props.name}</h1>
    }
}

1.3、手写简版myreact

实现原生标签节点、文本节点、函数组件和类组件的初次渲染

先用 Create React App 创建一个 React 项目,安装依赖并运行;

接着在 src/index.js 里边加上 这段代码查看一下版本号,保证自己的是17版本

console.log("version", React.version);

正是因为 React17 中,React会自动替换JSX为js对象,所以我们主要需要注释掉 src/index.js 中:

// import React from "react";
// import ReactDOM from "react-dom";

接着在src 下创建一个myreact文件夹,在里边创建一个 react-dome.js

// vnode 虚拟dom对象
// node 真实dom节点

// ! 初次渲染
function render(vnode, container) {
  // react17 可以自动转虚拟dom
  console.log("vnode", vnode);
  // vnode->node
  const node = createNode(vnode);

  // node->container
  container.appendChild(node);
}

// 创建节点
function createNode(vnode) {
  let node;
  const {type} = vnode;

  // todo 根据组件类型的不同创建不同的node节点

  if (typeof type === "string") { // 原生标签节点
    node = updateHostComponent(vnode);
  } else if (typeof type == "function") { // 函数组件 再次区分一下类组件和函数组件
    node = type.prototype.isReactComponent  
      ? updateClassComponent(vnode)
      : updateFunctionComponent(vnode);
  } else { // 文本节点
    node = updateTextComponent(vnode);
  }

  return node;
}

// 原生标签节点
function updateHostComponent(vnode) {
  const {type, props} = vnode;
  const node = document.createElement(type);

  console.log('document.createElement', node)

  // 更新节点部分
  updateNode(node, props); // 属性

  reconcileChildren(node, props.children); // 遍历children

  return node;
}

// 更新属性
function updateNode(node, nextVal) {
  Object.keys(nextVal)
    .filter((k) => k !== "children") // 过滤一下 children
    .forEach((k) => (node[k] = nextVal[k])); // 生成属性
}

// 文本节点
function updateTextComponent(vnode) {
  const node = document.createTextNode(vnode);
  return node;
}

// 函数组件
function updateFunctionComponent(vnode) {
  const {type, props} = vnode;
  // type 是一个 function
  const vvnode = type(props);
  // vvnode->node
  const node = createNode(vvnode);

  return node;
}

// 类组件
function updateClassComponent(vnode) {
  const {type, props} = vnode;
  // 类组件需要 new 
  const instance = new type(props);

  console.log('instance', instance);

  const vvnode = instance.render();

  console.log('vvnode', vvnode);
  // vvnode->node
  const node = createNode(vvnode);
  return node;
}

// 遍历children
function reconcileChildren(parentNode, children) {
  // 和源码一点写法区别,但是也是为了判断是否是数组
  const newChildren = Array.isArray(children) ? children : [children];

  for (let i = 0; i < newChildren.length; i++) {
    let child = newChildren[i];
    // vnode
    // vnode->node, node插入到parentNode
    render(child, parentNode);
  }
}

export default { render };


接着,还要在创建一个 src/myreact/Component.js 文件:

// 类组件必须继承自 Component 或者 PureComponent
function Component(props) {
  // 需要绑定一下this
  this.props = props;
}

// 做了一个 类组件的标记
Component.prototype.isReactComponent = {};

export default Component;


奥,不能忘了还要改动一下 src/index.js 文件内容:

// import React from 'react';
// import ReactDOM from 'react-dom';
import ReactDOM from './myreact/react-dom';
import Component from "./myreact/Component";
import './index.css';
// import App from './App';
import reportWebVitals from './reportWebVitals';

class ClassComponent extends Component {
  render() {
    return (
      <div>
        <p>类组件-{this.props.name}</p>
      </div>
    );
  }
}

export default ClassComponent;

function FunctionComponent(props) {
  return (
    <div>
      <p>函数组件-{props.name}</p>
    </div>
  );
}

const jsx = (
  <div className="myjsx">
    <h1>111111</h1>
    <h2>222222</h2>
    <h3>111111</h3>
    <a href="https://www.baidu.com/">百度</a>
    <FunctionComponent name="我是函数组件" />
    <ClassComponent name="我是类组件" />
  </div>
)

// 原生标签
// 文本节点
// 函数组件
// 类组件

ReactDOM.render(
  jsx,
  document.getElementById('root')
);

// console.log("version", React.version); // version 17.0.1

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

整体代码就是这样,具体过程就不在这里细致说明了,大家好好品一下代码,有疑问的可以联系我。

小结

1、React17 中,React会自动替换JSX为js对象。

2、js对象即vdom,它能够完整描述dom结构。

3、ReactDOM.render(vdom, container)可以将vdom转换为dom并追加到container中。

4、实际上,转换过程需要经过一个diff过程。

标签:node,核心,vnode,React,props,组件,原理,JSX
From: https://www.cnblogs.com/xiaofeng123aa/p/16736739.html

相关文章

  • 深度讲解React Props
    一、props的介绍当React遇到的元素是用户自定义的组件,它会将JSX属性作为单个对象传递给该组件,这个对象称之为“props”。函数声明的组件,会接受一个props形参,获取属性传递......
  • React核心原理与虚拟DOM
    React基础JSXconstelement=<h1>Hello,world!</h1>;JSX,既不是字符串也不是HTML,本质上是一个JavaScript的语法扩展,且更接近于JavaScript,是通过React.createElemen......
  • 线程池底层原理详解与源码分析(补充部分---ScheduledThreadPoolExecutor类分析)
    【1】前言本篇幅是对 线程池底层原理详解与源码分析 的补充,默认你已经看完了上一篇对ThreadPoolExecutor类有了足够的了解。 【2】ScheduledThreadPoolExecutor......
  • 深入剖析堆原理与堆排序
    堆的介绍完全二叉树:完全二叉树是满二叉树去除最后N个节点之后得到的树(\(N\geq0,N\inN^*\))大根堆:节点的父亲节点比自身节点大,比如根节点的值为\(8\),比其子节点\(7\)......
  • Vue、React、Angular之三国杀,web前端入坑第六篇(上)
      「懒癌引发血案 」目前前端技术栈发生了翻天覆地的变化,上篇刚写了只会jquery要失业,再不学新的你就要被淘汰,虽然有点危言耸听,不过现实情况确实是这样。vue、react、an......
  • Javaweb核心之注解开发Servlet
    这是一篇关于讲解如何正确使用51CTO博客-Markdown的排版示例,希望通过此,大家都能轻松上手,都能通过Markdown能#4注解开发Servlet4.1Servlet3.0规范4.2注解开发入门案......
  • Javaweb核心响应对象
    1响应对象1.1响应对象概述1.1.1关于响应响应,它表示了服务器端收到请求,同时也已经处理完成,把处理的结果告知用户。简单来说,指的就是服务器把请求的处理结果告知客户端......
  • javaweb核心之会话技术
    1会话技术1.1会话管理概述1.1.1什么是会话这里的会话,指的是web开发中的一次通话过程,当打开浏览器,访问网站地址后,会话开始,当关闭浏览器(或者到了过期时间),会话结束。举......
  • SpringBoot(概述、起步依赖原理分析、SpringBoot配置(配置文件分类、YAML))
    SpringBoot概述SpringBoot是由Pivotal团队提供用来简化Spring的搭建和开发过程的全新框架。随着近些年来微服务技术的流行,SpringBoot也成了时下炙手可热的热点技......
  • 107-11- HBase-2.4.4 架构设计和架构原理
                                                    重要部分 ......