你了解多少虚拟Dom?🤔React数据驱动思想和JSX语法细节

629 阅读6分钟

如今,无论是国内还是国外,React 成为主流框架的势头似乎无可阻挡。作为一名曾在 Vue 的世界里摸爬滚打的老兵,我再次投身于 React 的战场,感受到一股新的活力和挑战。

image.png

本文将直击核心——探讨“数据驱动视图”的理念,带您了解 React 如何超越传统的 JavaScript DOM 操作思维。我们将比较 Vue和 React 框架的不同体系结构——前者基于 MVC (模型-视图-控制器),而后者则采用了 MVVM(模型-视图-视图模型)架构。最后,我们将简单介绍 React 中 JSX 的基本细节,帮助您快速入门这一强大的前端工具。

01 | React 数据驱动视图思想

在 React 的世界里,一切皆由数据主宰。我们通过操作数据来触发视图的变化,当数据发生变动时,React 会根据最新的数据状态自动重新渲染页面。虽然 React 的底层实现仍然依赖于 DOM 来完成最终的视图呈现,但它巧妙地引入了虚拟 DOM 的概念,使得视图更新更加高效。(后文教你“忘记dom”)

1)虚拟 DOM 到真实 DOM 的渲染体系

React 构建了一套从虚拟 DOM 到真实 DOM 的转换机制,这一体系不仅让开发者可以专注于数据逻辑而不必担心频繁的 DOM 操作所带来的性能问题,还有效地 避免了由于直接操作 DOM 引起的重排和重绘,从而显著提升了应用的响应速度和用户体验。

2)性能优化与开发效率

得益于虚拟 DOM 的存在,React 应用在处理复杂界面更新时表现得尤为出色。它不仅提高了代码的可维护性和复用性,还为开发者带来了更高的开发效率。React 让我们可以更专注于构建用户交互和业务逻辑,而不必过多操心浏览器端的性能瓶颈。

3)设计理念

在这个过程中,React 并未完全摒弃对 DOM 的操作,而是选择了一种更为智能的方式:仅需在应用启动时绑定一次根元素(root),之后所有的视图变化都交由虚拟 DOM 处理,直到需要同步到实际的 DOM 为止。这样的设计既保证了高效的视图更新,又不失灵活性,真正实现了数据驱动视图的思想

// main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
// 
ReactDOM.createRoot(document.getElementById('root')).render(
    <App /> 
)

React里面绑定一次dom ReactDOM.createRoot(document.getElementById('root'));

这就不得不说一下React16和现在使用的React18 创建和使用 根root的区别了

  • React 16

在 React 16 中,创建和更新应用的根通常使用 ReactDOM.render 方法。这个方法接受两个参数:一个是要渲染的 React 元素,另一个是 DOM 容器节点。例如:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const container = document.getElementById('root');
ReactDOM.render(<App />, container);

想要更新应用程序的状态并重新渲染时,需要再次调用 ReactDOM.render,传入新的 React 元素和相同的容器节点。这会导致整个组件树的替换,尽管 React 的内部优化可以避免不必要的 DOM 操作。

  • 但是But React 18

React 18 引入了新的 Root API,提供了 createRoot 方法来替代旧的 ReactDOM.render。新的 API 更加明确地将创建根和渲染操作分开,并且为并发特性和其他新功能铺平了道路。使用 createRoot 创建根之后,可以通过调用根对象上的 render 方法来初始化或更新应用:

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
const root = createRoot(container); // 创建根
root.render(<App />); // 初始渲染

如果需要在服务端渲染的应用上进行客户端水合(hydration),则应使用 hydrateRoot 而不是 createRoot

import { hydrateRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
const root = hydrateRoot(container, <App />); // 水合并渲染

总的来说,React 18 的新 Root API 是为了提供更好的性能、灵活性和开发者体验

02| MVC 与 MVVM 体系

React 框架采用的是 MVC 体系,而 Vue 框架采用的是 MVVM 体系。

  • MVC

    Model(模型) :数据层。

    View(视图) :视图层。

    Controller(控制器) :控制层。

    • 需要按照专业的语法去构建视图(页面):React 中是基于 JSX 语法来构建视图的。
    • 构建数据层:但凡在视图中,需要“动态”处理的(需要变化的,不论是样式还是内容),我们都要有对应的数据模型。
    • 控制层:当我们在视图中(或者根据业务需求)进行某些操作的时候,都是去手动修改相关的数据,然后 React 框架会按照最新的数据,重新渲染视图,以此让用户看到最新的效果!

这里附上‘Max极客菌’对React的MVC图解 image.png

  • MVVM

    Model(模型) :数据层。

    View(视图) :视图层。

    ViewModel(视图模型) :视图监听层。

    • 数据驱动视图的渲染:监听数据的更新,让视图重新渲染。
    • 视图驱动数据的更改:监听页面中表单元素内容改变,自动去修改相关的数据

这里附上‘Max极客菌’对Vue的MVVM图解

image.png

03| 忘记 DOM 操作

在 React 中,我们不需要直接操作 DOM,而是通过 JSX 来定义组件和数据绑定。

这里后续还会出一期JSX文章,这里先简单讲解jsx的细节

  • JSX:JavaScript 和 XML 的混合语法,类似于 HTML。常见使用有:

    • 变量/函数:{text}
    • 数学运算:{1+1} -> {2} {x+y}
    • 判断:三元运算符 {i===1?'OK':'NO'}
    • 循环:借助于数组的迭代方法处理 {map}
  • JSX 构建视图的基础知识

    • 创建 .jsx 文件,支持 JSX 语法。
    • 在 HTML 中嵌入 JSX 表达式,需要基于 {} 胡子语法。(花括号)
    • 在 ReactDOM.createRoot() 的时候,不能直接把 HTML/BODY 做为根容器,需要指定一个额外的盒子(例如:#root)。
    • 出现多个根节点则报错:相邻 JSX 元素必须被包裹在一个闭合标签内。
    • React 提供了一个特殊的节点(标签):React.Fragment 空文档标记标签 <></>

04| React 代码示例

建立App.jsx文件,在前文提到的main.jsx 根文件导入这个文件。

值得提一嘴的是,后缀.jsx不同于.js

// APP.jsx 文件
import React from 'react'
import React from 'react'
// 全局
let styObj = {
  fontSize: '20px',
  color: 'red'
}

function App() {
   const [count, setCount] = useState(0)
    return(
      // React.Fragment 空标签替代‘div’,不增加一个层级
      <>
       {/* 渲染按钮 */}
        <button type="button" onClick={() => {
              setCount(count + 1)
        }}>按钮{count}</button> 
        <br />
        <h2 style={styObj}>12.22早安,舒服!!</h2>
        {/*jsx统治区注释 直接使用虚拟dom对象 */}
        {React.createElement('button',null,'按钮')}
      </>
    )
}

export default App
PixPin_2024-12-22_15-34-17.gif

当然渲染按钮进行状态更新时,可以使用函数进行封装某种操作,也算是代码进行复用吧

import { useState } from 'react'
import React from 'react'

function App() {
  // useState 钩子函数,两个状态变量,count 是状态值,setCount 是更新状态值的函数
    const  [count, setCount] = useState(0)
    const increment = () => {
    // 三种都行,推荐第一种,第三种return还要{},适合多条语句
      // setCount(count + 1)
      // setCount(preCount => preCount + 1)
      setCount(preCount  => {return 
        preCount + 1
      });
    }
    return(
      <>
        <button type="button" onClick={increment}>按钮{count}</button> 
      </>
    )
}

export default App

我相信看到这里的小伙伴中肯定会出现绝世高手~

希望这篇文章对大家有帮助,欢迎评论区探讨学习,学会的话也还请给本文一个点赞支持哦~