面试记录专刊:第18天

166 阅读5分钟

今日复习计划:不可变量、React18

一. 前端知识

1. React中使用不可变量

为什么React需要不可变量
import { useState} from "react";
import { cloneDeep } from "lodash";

export default function App() {
  const [obj, setObj] = useState({ name: "cxm" });

  return (
    <div>
      <button
        onClick={() => {
          let newObj = cloneDeep(obj);
          newObj.name = "cxm2";
          setObj(newObj);
        }}
      >
        点击
      </button>
      <div>{obj.name}</div>
    </div>
  );
}

在上诉代码中,stateobj为引用型数据类型,当我们需要修改obj的熟悉时我们不可以直接进行修改。在以往的做法中,我们需要使用deepClone之类的库进行创建一个新的对象。当对象较大时,十分影响我们系统的性能。使用immer不可变量既可以优化代码又可以优化性能。

import { useImmer } from "use-immer";

export default function App() {
  const [obj, setObj] = useImmer({ name: "cxm" });

  return (
    <div>
      <button
        onClick={() => {
          setObj((draft) => {
            draft.name = "cxm2";
          });
        }}
      >
        点击
      </button>
      <div>{obj.name}</div>
    </div>
  );
}

2. React18

2.1 RenderApi

18前:


ReactDom.Render(root,<App/>,()=>{
  console.log('渲染完成')
})

18:

ReactDom.createRoot(root).render(()=>{
  console.log('渲染完成')
  return <App/>
})

2.2 setState批量处理

react18之前,在react事件中的setState会批量处理。
// React18之前
import { useState } from "react";
import "./styles.css";

export default function App() {
  console.log("app render");  // 会更新1次。

  const [count1, setCount1] = useState(1);
  const [count2, setCount2] = useState(1);
  return (
    <div className="App">
      <button
        onClick={ () => {
          setCount1(count1 + 1);
          setCount2(count2 + 1);
        }}
      >
        更新
      </button>
    </div>
  );
}  
react18之前,在setTimeoutPromise等事件中setState则不会进行批量处理
// React18之前
import { useState } from "react";
import "./styles.css";

export default function App() {
  console.log("app render");  // 会更新2次。

  const [count1, setCount1] = useState(1);
  const [count2, setCount2] = useState(1);
  return (
    <div className="App">
      <button
        onClick={ () => {
          setCount1(count1 + 1);
          setCount2(count2 + 1);
        }}
      >
        更新
      </button>
    </div>
  );
}  
React18里,无论在任何时间中,setState都会被批量处理,除了一下情况
// React18里
import { useState } from "react";
import "./styles.css";

export default function App() {
  console.log("app render");  // 会更新2次。

  const [count1, setCount1] = useState(1);
  const [count2, setCount2] = useState(1);
  return (
    <div className="App">
      <button
        onClick={aync () => {
          await setCount1(count1 + 1);  // await会导致不会批量处理
          setCount2(count2 + 1);
        }}
      >
        更新
      </button>
    </div>
  );
} 
  • 在 18 之前,只有在react事件处理函数中,才会自动执行批处理,其它情况会多次更新
  • 在 18 之后,任何情况都会自动执行批处理,多次更新始终合并为一次,除非使用了aync await进了打断

2.3 并发模式

lane

React17起,React对事件的优先级判断从expirationTime更新为lane。用于判断事件优先级,使用二进制表示,对应十进制为0-31,优先级依次下降。

export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;
export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;
export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;
const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;
export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;
const TransitionShortHydrationLane: Lane = /*           */ 0b0000000000000000001000000000000;
const TransitionShortLanes: Lanes = /*                  */ 0b0000000000000011110000000000000;
const TransitionLongHydrationLane: Lane = /*            */ 0b0000000000000100000000000000000;
const TransitionLongLanes: Lanes = /*                   */ 0b0000000001111000000000000000000;
const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;
export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;
const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;
export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;
export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;
useTransition
import { useState, useTransition, useLayoutEffect } from "react";

function App() {
  const [value, setValue] = useState("");
  const [isPending, startTransition] = useTransition();

  const handleValueChange = (e) => {
    startTransition(() => setValue(e.target.value));
  };

  return (
    <div className="container">
      <input onChange={handleValueChange} />
      <div className="list">
        {Array(100000)
          .fill("a")
          .map((item) => (
            <div>{value}</div>
          ))}
      </div>
    </div>
  );
}
export default App;

setValueuseTransition的回调中执行,触发了并发更新。被setTransition包含的setState的UI更新会被标记为不紧急更新,反正会优先执行紧急事件:onClickonScroll等。

2.4 Suspense与React.lazy实现代码分包

import React, { Suspense } from "react";
import "./styles.css";
const OtherComponent = React.lazy(() => import("./otherComponent"));

export default function App() {
  return (
    <div className="App">
      <Suspense>
        <OtherComponent></OtherComponent>
      </Suspense>
    </div>
  );
}

二. 算法

三. 面经

四. 其他

He said, one day you’ll leave this world behind,
so live a life you will remenber