React|笔记

116 阅读5分钟

React

React 使得 UI 开发更倾向于声明式编程,但并没有完全排除指令式编程。在实际开发中,两者往往是结合使用的,以适应不同的开发需求和场景

响应式系统与React

React的历史与应用

历史

image-20241120205419597.png

image-20241120210210713.png

image-20241120210251408.png

应用
  • 前端应用开发,如Facebook, Instagram,Netflix网页版
  • 移动原生应用开发,如Instagram,Discord,Oculus
  • 结合Electron,进行桌面应用开发
  • 结合React Three Fiber库,创建和控制3D内容。

React的设计思路

UI编程痛点
  1. 状态更新,UI不会自动更新,需要手动地调用DOM进行更新。
  2. 欠缺基本的代码层面的封装和隔离,代码层面没有组件化。
  3. UI之间的数据依赖关系,需要手动维护,如果依赖链路长,则会遇到"Callback Hell”。

image-20241120210631659.png

响应式与转换式

image-20241120211407705.png

响应式系统

事件->执行既定的回调->状态变更

前端UI

事件->执行既定的回调->状态变更->UI更新

对React的期望
  1. 状态更新,UI自动更新。
  2. 前端代码组件化,可复用,可封装。
  3. 状态之间的互相依赖关系,只需声明即可。
组件化
  • 组件是组件的组合/原子组件
  • 组件内拥有状态,外部不可见
  • 父组件可将状态传入组件内部

image-20241120212142365.png

[当前价格]属于Root节点 不好 状态上升

[当前价格]由Root的函数onChangeValue()向下传递到型号选择等

  • React是单向数据流->父传子状态 子改变父状态->父把函数传给子 子执行函数
组件设计
  • 组件声明了状态和UI的映射
  • 组件有Props/State 两种状态
  • “组件”可由其他组件拼装而成
组件代码
  • 组件内部拥有私有状态State
  • 组件接受外部的Props状态提供复用性
  • 根据当前的State/Props,返回一个UI
生命周期

image-20241120213810821.png

React(hooks)的写法

import React, { useState } from 'react' ;
​
function Example() {
//声明一个新的 state 变量,我们将其称为 “count”
const [count, setCount] = useState(0);//声明一个新状态需要手动调用useState函数 这个函数传入一个状态的初始值,返回一个数组 第一个是状态本身 第二个函数是这个状态的setCount 
    
    //类似于组件生命周期中的componentDidMount和componentDidUpdate:
    useEffect(() => {
        //使用浏览器API更新文档标题
        document.title = 'You clicked ${count} times';
    });
​
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    );
}
import { Switch, Route } from "@modern-js/runtime/router";
import { useState, useEffect } from "react";
​
import "./App.css";
​
//父组件给子组件传入了一个名叫title的值
const Subcomponent = (props) => {
    <h1>{props.title}</h1>
}
​
//useState和useEffect是Hooks
const App = () => {
    const [x, setX] = useState(0);
    const [y, setY] = useState(0);
    const sum = x + y;
​
    //副作用: 每次x,y变化时,更新document.title
    useEffect(() => {
        document.title = `${x}-${y}`;
    });
​
    return (
        <Switch>
            <Route exact={true} path="/">
                <div className="container">
                    <main>
                        <h1>Welcome to 青训营!</h1>
                        <Subcomponent title={'青训营'} />
                        <h1>
                            x={x},y={y}{' '},和是{sum}
                        </h1>
                        <div className="grid">
                            <button type="button" onClick={() => setX(x + 1)}>
                                x+1
                            </button>
                            <button type="button" onClick={() => setY(y + 1)}>
                                y+1
                            </button>
                        </div>
                    </main>
                </div>
            </Route>
            <Route path="*">
                <div>
                    404
                </div>
            </Route>
        </Switch>
    );
}
​
export default App;
useEffect

传入一个函数,和一个数组,数组是状态的数组,称作依赖项,该函数在mount时,和依赖项被set的时候会执行。

useEffect是有“副作用”的函数,要传入useEffect来执行。副作用代表除了单纯的计算之外,还要做其它的一些事情,比如网络请求,更新DOM,localStorage,存储数据等。

Hook 使用法则
  1. 不要在循环,条件或嵌套函数中调用Hook

React的实现

问题
  1. JSX 不符合 JS 标准语法

    •   const Test = (props) => {
        ​
            const { url } = props;
        ​
            return (
                <div className="root">
                    <img src={url} />
                </div>
            )
        }
      

      会自动转换成js

        const Test = props => {
            const {
                url
            } = props;
            return React.createElement(
            "div",
            { className: "root" },
            React.createElement("img", { src: url })
          );
        };
      
  2. 返回的 JSX 发生改变时,如何更新DOM DOM的操作比较耗费性能

    • Virtual DOM(虚拟 DOM)

      Virtual DOM是一种用于和真实 DOM同步,而在JS内存中维护的一个对象,它具有和DOM类似的树状结构,并和DOM可以建立一一对应的关系。

      它赋予了React 声明式的API:您告诉React 希望让 UI是什么状态,React就确保DOM匹配该状态。这使您可以从属性操作、事件处理和手动DOM更新这些在构建应用程序时必要的操作中解放出来。

    image-20241122190756789.png

    • 如何计算Diff

      • 权衡:更新次数少 计算速度快

      • 完美的最小Diff算法,需要O(n^3)的复杂度。

        牺牲理论最小Diff,换取时间,得到了O(n)复杂度的算法:Heuristic O(n) Algorithm

      image-20241122191452305.png

  3. State/Props更新时,要重新触发 render 函数

React状态管理库

  • 核心思想

    image-20241122191720264.png

坏处:降低组件的复用性 组件与外部数据管理的Store强耦合性

  • 推荐

    • redux

      • 集中式管理和严格的数据流
    • XState

      • 处理复杂的状态转换和异步逻辑
    • mobx

      • 更灵活和自动的状态追踪
    • recoil

      • 简单且与React紧密集成的状态管理解决方案
  • 状态机

    类似红绿灯

    image-20241122193003296.png

当前状态->收到外部事件->迁移到下一个状态

  • 哪些东西适合放在状态管理库

    • 被全局所使用的数据放在Store
    • 仅被该组件所使用的数据不用放
  • 例子:

    import { model } from '@modern-js/runtime/model';
    ​
    //状态机
    const countModel =model('count').define({
        //状态共享
        state: {
            x: 0,
            y: 0,
        },
        actions: {
            incrementX(state) {
                state.x += 1;
            },
            incrementY(state) {
                state.y += 1;
            },
        },
    });
    ​
    export default countModel;
    
    import { Switch, Route } from "@modern-js/runtime/router";
    import { useState } from "react";
    import { countModel } from "./CountModel";
    ​
    import "./App.css";
    ​
    const Subcomponent = props => {
        const [{ x, y }] = useModel(countModel);
        return (
            <h1>{x} and {y}</h1>
        );
    }
    ​
    //useState和useEffect是Hooks
    const App = () => {
        const [{ x, y }, { incrementX, incrementY }] = useModel(countModel);
        const sum = x + y;
    ​
        return (
            <Switch>
                <Route exact={true} path="/">
                    <div className="container">
                        <main>
                            <h1>Welcome to 青训营!</h1>
                            <Subcomponent title={'青训营'} />
                            <h1>
                                x={x},y={y}{' '},和是{sum}
                            </h1>
                            <div className="grid">
                                <button type="button" onClick={() => incrementX()}>
                                    x+1
                                </button>
                                <button type="button" onClick={() => incrementY()}>
                                    y+1
                                </button>
                            </div>
                        </main>
                    </div>
                </Route>
                <Route path="*">
                    <div>
                        404
                    </div>
                </Route>
            </Switch>
        );
    }
    ​
    export default App;
    

应用级框架科普

  • next.js

    • 硅谷明星创业公司 Vercel的React开发框架,稳定,开发体验好,支持 Unbundled Dev, SWC 等,其同样有Serverless 一键部署平台帮助开发者快速完成部署。口号是“Let'sFaster"

    • 内置的能力

    image-20241122200636678.png

  • MODERN.js

    • 字节跳动 Web Infra 团队研发的全栈开发框架,内置了很多开箱即用的能力与最佳实践,可以减少很多调研选择工具的时间。

    • 内置的能力

      image-20241122200716674.png

      image-20241122200736446.png

  • Blitz

    • 无 API 思想的全栈开发框架,开发过程中无需写API 调用与 CRUD 逻辑,适合前后端紧密结合的小团队项目。