# vue 高级用法

# watchEffect 函数

立即运行传入的函数,同时自动追踪其依赖,并在依赖更改时重新执行。
watchEffect 的返回值是用于清除该副作用的函数。

const data = ref(0);
const stop = watchEffect(()=>console.log(data.value,'data变化了'));
stop(); // 清除响应性监听

watchEffect 的第二个参数是 options 配置项,可以配置 flush、onTrack 函数和 onTrigger 函数

watchEffect(()=>{},{
    flush:"post", //flush 配置项配置回调函数的刷新时机,post 会在 DOM 渲染之后触发、sync 会在 vue 进行任何更新之前进行触发
    onTrack(e){debugger};
    onTrigger(e){debugger};
})

# watchSyncEffect 函数

回调函数会在 DOM 渲染之后触发,相当于 watchEffect 中配置了 fulsh:"post"

# watchSyncEffect 函数

回调函数在 vue 进行任何更新之前触发,相当于 watchEffect 中配置了 fulsh:"sync"

# effectScope 函数

effectScope 函数创建一个 effect 作用域,可以捕获其中所创建的响应式副作用(即计算属性和侦听器),这样捕获到的副作用可以一起处理。

const scope = effectScope();
scope.run(()=>{
  const doubled = computed(() => counter.value * 2)
  watch(doubled, () => console.log(doubled.value))
  watchEffect(() => console.log('Count: ', doubled.value))
})
scope.stop(); // 清除掉作用域内所有的 effect

# getCurrentScope 函数

获取当前活跃的 effect 作用域

# onScopeDispost 函数

在当前活跃的 effect 作用域上注册一个处理回调函数。当相关的 effect 作用域停止时会调用这个回调函数。

# shallowRef 函数

用于浅层响应式,避免深层比较带来的效率问题

# triggerRef 函数

强制触发依赖于一个浅层 ref 的副作用,通常在对浅引用的内部值进行深度变更后使用

# customRef 函数

customRef 函数创建一个自定义的 ref,显示声明对其依赖追踪和更新触发的控制方式。预期接受一个工厂函数作为参数,这个工厂函数接收 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象

// 定义一个返回懒执行响应式数据的函数
function useDebouncedRef(value,delay=200){
    let timeout;
    return customRef((track,trigger)=>{
        return {
            get(){
                track();
                return value;
            },
            set(newValue){
                clearTimeout(timeout);
                timeout = setTimeout(()=>{
                    value = newValue;
                    trigger();
                },delay)     
            }
        }
    })
}

# shallowReactive

shallowReactive 是 reactive 的浅层作用形式,只有跟级别的属性是响应式的,属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。

# shallowReadonly

shallowReadonly 是 readonly 的浅层作用形式。

# isRef 函数

用于检查某个值是否是 ref

# unref 函数

如果是 ref,返回 ref 内部的值,否则返回参数本身

# toRef 函数

  • 传入 ref 返回 ref 本身
  • 传入 props.key 创建一个只读的 ref
  • 传入 number 或者 string 相当于 ref 函数
  • 传入响应式数据和键值会封装为一个 ref,但是相比于直接封装 ref 来说,会与源属性进行同步
    const state = reactive({foo:1,bar:2});
    const fooRef = toRef(state,'foo'); //fooRef 会和 state 的响应性相关联
    const fooRef2 = ref(state.foo); //fooRef2 不会和 state.foo 的响应性相关联

# toRefs 函数

vue3 中的 ref 将 reactive 响应性绑定到.value 属性上,其本质就是为了防止开发者错误的将响应式数据进行解构后的变量又其当作响应式数据。所以加了一层隔离。toRefs 函数也是用于解决这个问题。
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef 创建的

const state = reactive({
  name:"张三",
  age:18  
})
const stateAsRefs = toRefs(state);
state.name = "李四"; //toRefs 返回的响应式数据和原响应式数据相互关联
console.log(stateAsRefs.name.value) // 李四
//toRefs 的存在是为了保证 reactive 响应式被解构之后仍然存在响应性
const {name,age} = toRefs(state)
// 解构之后的 name 和 age 都是响应式对象

# toRaw 函数

toRaw 返回由 reactive、readonly、shallowReactive 或者 shallowReadonly 创建的代理对应的原始对象(栈赋值),返回的对象不再具有响应式,栈赋值时不会影响到页面的展示,但如果改变该对象的堆中的属性,原对象的依赖项也会随之变化

# markRaw 函数

将一个对象标记为不可被转为代理,返回该对象本身

# toValue 函数

将值、函数、或响应式数据规范化为普通值,toValue (ref (1)) ---> 1

# isProxy 函数

检查一个对象是否是由 reactive、readonly、shallowReactive 或者 shallowReadonly 创建的代理。

# isReactive 函数

检查一个对象是否是由 reactive 或 shallowReactive 创建的代理

# isreadonly 函数

检查传入的值是否是只读对象,只读对象的属性可以更改,但是不能通过传入的对象直接赋值。

# Transition 组件

  • 使用过渡样式
  • mode 属性定义动画进行的顺序,常用 out-in
  • name 属性定义动画的名称,配合 css 使用
  • appear 属性初次渲染过渡
  • 生命周期钩子用于自定义过渡中执行过程,依次是 before-enter,enter,after-enter,enter-cancelled,before-leave,leave,leave-cancelled, 可用的参数有 el 和 done ()
  • css 样式过渡:name-enter-from\name-enter-active\name-enter-to\name-leave-from\name-leave-active\name-leave-to 定义对应时期的样式 (只对可以过渡变化的样式生效)

# TransitionGroup 组件

  • TransitionGroup 组件用于对 v-for 列表中的元素或组件插入 、移除或顺序变化添加动画效果.
  • 属性和生命周期和 Transition 一样
  • tag 属性可以指定 TransitionGroup 为一个容器

# KeepAlive 组件

  • KeepAlive 用于在多个组件间切换状态时缓存被移除的组件实例 (使组件保存状态)
  • KeepAlive 的原理就是当 KeepAlive 中的组件被移除时,用变量将整个组件缓存起来,需要切换时直接使用缓存起来的变量
  • include 和 exclude 属性用于包含或排除对应的组件 name
  • KeepAlive 的组件包含 onActivated 和 onDeactived 两个生命周期钩子

# Teleport 组件

  • Teleport 组件,用于将组件内部的一部分模板传送到外部结构中去
  • to 属性用于指定传送到的组件或者 DOM 元素

# Suspense 组件

  • Suspense 组件用于显示异步组件加载中的显示状态
  • Suspense 组件中 fallback 具名插槽用于显示加载内容
  • Suspense 组件嵌套 Suspense 组件时,给内部 Suspense 组件加上 suspensible 属性表示为异步组件,否则则会被父级 Suspense 组件视为同步组件

# watch 的 options 配置项

watch 的 options 配置项中可以使用:

  • immediate, 为 true 时会在初始化时立即执行一次
  • deep, 为 true 时会深度监听对象堆中变化
  • flush, 调整回调函数的执行时机
  • once, 回调函数只会执行一次
  • onTrack 函数,当响应式被收集时进行触发只在开发模式下有效
  • onTrigger 函数,当依赖项变更时进行触发只在开发模式下有效

# computed 的 options 配置项

  • onTrack 函数,当响应式被收集时进行触发只在开发模式下有效
  • onTrigger 函数,当依赖项变更时进行触发只在开发模式下有效

# vue 对 jsx 的支持友好

  • 在 vue 中也可以很方便的去集成 jsx 或 tsx 语法,tsx 语法需要在 tsconfig.json 中配置:jsx:preserve,最终的 jsx 语法会被转换为 h 函数
  • 对于事件和案件修饰符,可以使用 vue 中的 withModifiers 函数

# vue 对 web component 的支持友好

  • 在 Vue 应用中使用自定义元素基本上与使用原生 HTML 元素的效果相同
  • 需要在构建工具中配置 compilerOptions.isCustomElement 这个选项
  • 传递 ODM 属性时,需要使用 v-bind 绑定,通过.prop 修饰符进行设置
    <my-element :user.prop="{ name: 'jack' }"></my-element>
    <!-- 等价简写 -->
    <my-element .user="{ name: 'jack' }"></my-element>
  • 使用 vue 构建 web component 需要使用 defineCustomElement 这个方法定义出组件,然后通过 customElement.define 这个方法将 vue 组件添加标签到 HTML 中

# web component 的优缺点

  • 全部使用自定义元素来构建应用的方式可以使得应用永不过时和多平台、框架共享
  • 但是设想与显示总是存在偏差:
    • 1、原生 web component 并不具备响应式的系统
    • 2、原生 web component 并不具备一个声明式的、高效的模板系统
    • 3、SSR 渲染时,web component 需要在 node.js 中模拟出 DOM,这将增大服务器端的压力
    • 4、当下要想使用 shadow DOM 书写局部作用域的 CSS,必须要将样式嵌入到 JavaScript 中才可以在运行时注入到 shadow root 上,这将导致 SSR 场景下需要渲染大量重复的样式标签。

# vue 结合 js 动画库

  • 以 gsap 为例,vue 结合 js 动画库实现动画效果时,不能直接对响应式变量进行动画设置,因为是响应式完成之后才被监听到,此时响应式变量已经是最新的值,所以应该再来一个响应式变量中转一下,页面动画效果绑定的是中转的变量。

# vue 生命周期

  • vue3 中 setup 替代了 beforeCreate 和 created
  • beforeMount、monuted、beforeUpdate、updated、beforeUnmount、unMounted
  • 错误捕获钩子:onErrorCaptured,如果在 onErrorCaptured 中抛出一个错误,则会被 app.config.errorHandler 捕获到
  • 开发时钩子:onRenderTracked(组件渲染过程中追踪到响应式依赖时调用)和 onRenderTriggered(当响应式依赖触发了组件渲染时调用)
  • SSR 钩子:onServerPrefetch(注册一个异步函数,在组件实例在服务器上被渲染之前调用),SSR 渲染时,组件作为初始请求的一部分被渲染,这时可以在服务器上预请求数据,因为它比在客户端上更快。
  • keepAlive 组件下的钩子:onActivated 和 onDeactivated 两个,用于当组件激活和失活时调用

# React

# Fiber

Fiber 架构:Fiber 架构是一个增量渲染,架构风格类似协程,Fiber 架构出现的原因是由于 JS 单线程执行的特性当遇到繁琐的执行任务时,原来 React15 的协调过程就会很长,从而延迟 DOM 的渲染更新,进而出现掉帧。Fiber 架构将任务分给一个个的迭代器进行执行,并且使迭代器的执行过程发生在浏览器的空闲时间,从而最大程度利用了这部分资源,使得 UI 渲染不会被大量执行任务所阻塞。
fiber 树:fiber 树是一个数据结构,架构在虚拟 DOM 转换到真实 DOM 之间,在 Fiber 结构中的任务是可以中断执行的,继续执行时会丢弃掉原来的工作从头再来,并且重新执行中断的任务,对用于来说也是无感的,因为 Fiber 架构的视图更新是后缓冲区视图 替换 前缓冲区视图的过程。
由于 Fiber 架构下的 reconciler 协调阶段是异步可中断的,且会被反复重新执行,使得反复执行时有可能触发的生命周期钩子被废弃,例如:componentWillMount、componentWillReceiveProps、componentWillUpdate 这些。
fiber 树(fiber 节点构成的树状链表)的遍历与执行:

  • fiber 树遍历:children 子节点 ----sibling 兄弟节点 ----return 父节点
  • fiber 树的执行:遍历到最底层子节点 A----A 的兄弟节点 -----A 的父节点 B------B 的兄弟节点(深度优先遍历)

fiber 节点(对象):一个 fiber 节点对应一个 React 组件,fiber 节点里包含了组件的 work 任务等信息,比如组件的 Hooks 执行逻辑(它是一个链表,可以通过 memorizedState 拿到跟节点,Hooks 执行逻辑里面存储了 state)、生命周期、对于 html 组件的增删改查等副作用
当执行 fiber 节点中的任务时,每次执行完之后 React 就会检查当前帧还剩多少时间,没有时间就会将控制权让出去。

# fiber 树、VDOM 和 diff 算法

fiber 树就是 VDOM,fiber 树的变化反映了组件的状态变化
diff 算法的本质就是:对比 current fiberJSX对象 生成 work-in-progress-fiber
即使是最前沿的算法,完全对比两棵树的时间复杂度也需要 O (n^3), 其中

# Hooks

注意:react Hooks 只能在件顶层进行调用并且不能写在条件判断中,这是因为 hooks 以链表的形式存放在 fiber 节点中(类组件的副作用也存在 fiber 节点中),每次更新时会基于链表的顺序进行调用,而调用 hooks 所产生的 state 就存在于 hooks 节点之中,如果 hooks 写在条件判断中则导致 hooks 链表执行混乱,使得状态更新出错。

# workLoop 工作循环

workLoop 中会根据当前帧的剩余时间去执行 fiber 节点中的任务,如果时间不够就将控制权转给 UI 渲染,并保存当期的执行上下文 (包括当前 fiber 节点的状态), 当 UI 渲染完毕后,恢复其执行 (执行到一半的任务会重新执行)

# 双缓冲策略

双缓冲策略是用于减少组件渲染过程中的闪烁和卡顿。双缓冲策略是对于 fiber 树来说的
双缓冲策略会维护两个 Fiber 树:Work-in-progress-fiber 树 和 current-fiber 树。协调过程中 React 会比较新旧两个 fiber 树的差异,从而确定哪些组件需要更新。一旦新的 fiber 树构建完成,React 就会使用 diff 算法去更新真实 DOM。更新完成后会将工作 Fiber 树的根节点与当前 Fiber 树的根节点进行交换,这个过程叫做提交。

# Fiber 更新的三个阶段

  • 开始阶段: ReactFiberBeginWork
    • 这个阶段 react 需要决定哪些组件需要更新、哪些组件可以复用、哪些组件需要被挂载或卸载
    • React 通过比较新旧 Fiber 树来确定变化,这个过程称为协调算法(Reconciliation)。
    • 此阶段会创建一个新的工作进度树(work-in-progress tree),表示 UI 的最新状态。
    • 这个阶段是可中断的,React 可以决定挂起渲染过程,稍后再恢复。
  • 完成阶段: ReactFiberCompeleteWork
    • 这个阶段发生在实际将更新应用到屏幕之前。
    • React 执行生命周期方法,如 getSnapshotBeforeUpdate,允许组件捕获当前的 DOM 状态或执行捕获操作。
    • 这个阶段用于执行那些需要在提交前知道布局效果的副作用,例如,测量组件的尺寸或位置。
  • 提交阶段: ReactFiberCommitWork
    • 这是实际将更改应用到真实 DOM 的阶段。
    • React 处理所有副作用,如 componentDidMount、componentDidUpdate 和 componentWillUnmount 生命周期方法。
    • 更新 DOM 元素和属性,添加或删除 DOM 节点,以确保真实 DOM 与工作进度树同步。这个阶段是连续的,不能被中断,因为 DOM 更新通常需要原子性地完成。

# 为什么 hooks 不能写在条件判断

hooks 函数最终会被存在组件对应的 fiber 节点的 memoizatedState 中,组件每次更新会按照顺序执行 (hooks 里面存着 state 状态),如果组件每次更新时的 hooks 链表顺序乱了,就会导致 state 对应不上、状态混乱

# react 不可变数据

例如当 setData (data + 1) 副作用在组件内连续调用三次时,其实是相当于只调用了一次,这就是 react 不可变数据或者说:当前快照只能操作当前快照的值;
出现 react 不可变数据现象的原因是由于: hooks 执行逻辑最后会以链表的形式存储在 fiber 节点之中,而那里面不会有类似上面 data 这样的变量,data 会在 hooks 执行过程被存储之前转换为其具体的值,上例正确的写法是写成一个函数,这样在存储 hooks 执行逻辑到 fiber 节点中时就会先执行传入的这个函数,例如应该写成:setData (()=>data+1)

# ReactFiberLane 模型 (并发模式)

React 并发模式:首先,并发和并行不一样,并行是同一时刻多件事情同时进行, 而并发是只要一段时间内同时发生多件事情就行 。React 并发模式允许多个状态在同一时间段进行更新,Fiber 节点上的 Lanes 标记告诉 React 如何并发处理这些状态更新。
为了让高优先级的更新能先渲染,react 实现了并发模式。React 中更新 State 有两种模式:同步模式是循环处理 fiber 节点,并发模式多了个 shouldYield 的判断,每 5ms 打断一次,也就是时间分片。并且之后会重新调度渲染。
React 并发是指:对于每一个次 State 状态变化要执行的 Hooks 链表的更新,再循环执行时都会判断一下是否需要打断这次循环,从而将更新让给其他任务
这些并发特性的 api 都是通过设置 Lane 实现的,react 检测到对应的 Lane 就会开启 带有时间分片的 workLoopConcurrent 循环 。时间分片的 workLoop + 优先级调度,这就是 React 并发机制的实现原理。这就是 React 并发机制的实现原理。
基于 Lane 的优先级实现的 api, 例如:useTransition、useDeferredValue。当被用到 的时候,react 才会启用 workLoopConcurrent 带时间分片的循环。
被打断的 Hooks 链表的更新任务会被丢弃,由于没有渲染完所以需要再添加一个任务进任务队列
Lane 模型是 React 中的一种状态更新机制,目的是提高应用的性能和响应速度,核心思想是将 UI 中的状态变化抽象成一系列的 lane 变化,每个 lane 只描述了一个状态的变化,而不是一次完整得状态更新,这样可以使得状态变化更加清晰易于处理和维护
react 的并发模式的打断只会根据时间片,也就是每 5ms 就打断一次,并不会根据优先级来打断,优先级只会影响任务队列的任务排序。
React 通过 Scheduler 调度任务时,会先把 Lane 转为事件优先级,再把事件优先级转为 Scheduler 的五种优先级
所谓的并发渲染就是加了一个 5ms 一次的时间分片,react18 里同时存在着这两种循环方式,普通的循环和带时间分片的循环。也不是所有的特性都要时间分片,只有部分需要,如果这次 setState 更新里包含了并发特性,就是用 workLoopConcurrent,否则走 workLooSync 就好了。
比如上面有两个 setState,其中一个优先级高,另一个优先级低,那就把低的那个用 startTransition 包裹起来。就可以实现高优先级的那个优先渲染。 实现原理是 :在调用回调函数之前设置了更新的优先级为 ContinuousEvent 的优先级,也就是连续事件优先级,比 DiscreteEvent 离散事件优先级更低,所以会比另一个 setState 触发的渲染的优先级低,在调度的时候排在后面。这里设置的其实就是 Lane 的优先级:那渲染的时候就会走 workLoopConcurrent 的带时间分片的循环 ,然后通过 Scheduler 对任务按照优先级排序,就实现了高优先级的渲染先执行的效果。
React 使用 31 位二进制来表示优先级车道,一共 31 条,位数越小 (1 的位置越靠右) 表示优先级越高.
React 每次更新状态会将同类型的 Lane (通过位运算与) 合并形成 Lanes (通过位运算或), 然后从同类型的 Lanes 中找出优先级最高的事件
当一个 Fiber 节点需要更新时,React 会根据状态得变化创建更新类型,通过更新类型在该节点上标记相应的 Lanes, 指示了该节点的优先级类型,React 这种并发模式允许多个更新同时进行处理,React 会根据 Fiber 节点上的 Lanes 来决定哪些更新可以并发执行,以及他们的执行顺序,如果一个高优先级的更新需要立即处理,React 可以中断当前正在进行的低优先级更新,转而处理高优先级的更新。一旦高优先级的更新完成,之前中断的更新可以恢复。

# useState 钩子

创建可以直接更新的状态变量

# useReducer 钩子

与 useState 相似,创建状态变量,同时可以自定义 reducer(内部变量变化的调度机制)

function App() {
  // 注意:reducer 中的返回值就是新的 state
  const dataReducer = (state, aciton) => {
    switch (aciton) {
      case 0: return "你好";
      case 1: return "世界";
      case 2: return "你好世界";
      default: return "世界你好";
    }
  }
  const [data, dispatchData] = useReducer(dataReducer, "你好世界");
  return (
    <>
      <h1>{data}</h1>
      <button onClick={() => dispatchData(0)}>你好</button>
      <button onClick={() => dispatchData(1)}>世界</button>
      <button onClick = {()=>dispatchData(2)}>你好世界</button>
      <button onClick={() => dispatchData(3)}>世界你好</button>
    </>
  )
}

# createContext 和 useContext 钩子

createContext 和 useContext 直接使用相当于是创建可用的变量

const testContext = createContext("初始化数据")
// 在组件中就可以拿到 testContextData, 然后使用:
const testContextData = useContext(testContext);

createContext 和 useContext 高级用法本质上是依赖注入,他返回一个对象,对象的 Provider 属性是一个组件,用于注入数据(在 Provider 组件上绑定 value 属性),注入的数据可以通过 useContext 获取

const ThemeContext = createContext(null); // 创建 context
function MyPage() {
  const [theme, setTheme] = useState('dark');
  // 将依赖注入到组件内部
  return (
    <ThemeContext.Provider value={theme}>
      <ThemeText />
    </ThemeContext.Provider>
  );
}
// 组件内部使用注入的依赖
function ThemeText(){
  const themeData = useContext(ThemeContext);
  return <>{themeData}</>
}

# useRef 钩子

帮助引用一个不需要渲染的值 (不会触发组件重新渲染), 返回一个具有 current 属性的对象,通常用于保存 DOM 节点
注意,改变 ref 不会触发重新渲染,所以 ref 不适合用于存储期望显示在屏幕上的信息。如有需要,使用 state 代替。React 希望不要在渲染期间写入或者读取 ref.current, 如果不得不在渲染期间读取 或者写入,那么应该 使用 state 代替。

function App() {
  const inputFocus = (ref) => {
    ref.current.focus();
  };
  const inputRef = useRef(null);
  return (
    <><input ref={inputRef} />
      <button onclick={(e) => inputFocus(inputRef)}>聚焦输入框</button></>
  );
}
export default App

# useEffect 钩子

接收两个参数:副作用函数和依赖项数组,当依赖项变化时自动执行副作用函数,副作用函数的返回值是一个清理函数,会在每次组件更新前进行执行
useEffect 中第二个参数不传代表每次渲染组件后都执行一次,传空数组代表只会在第一次挂载后执行,传依赖项代表在 ** 依赖项变化时执行。** 并且默认组件挂载时就会自动执行一次,以便可以读取到依赖项
useEffect 依赖项中传入 ref 通常是无效的,因为 ref 相当于不使用渲染赋值的 state 状态,传递过去的 ref 引用始终相同(不随着快照的渲染而变化)。
某些逻辑不能放在 effect 中执行,因为 effect 的执行是和组件渲染强绑定的(例如不能在 effect 中写购买商品的逻辑,这样会导致组件以任何方式被渲染都会执行购买逻辑,这样是不对的)
effect 中 return 的函数会在下一次 effect 执行前被执行,常用于执行清理函数(清除定时器等)

# useLayoutEffect 钩子

在浏览器重新绘制屏幕前执行,可以在此处测量布局。

# useInsertionEffect 钩子

在 React 对 DOM 进行更改之前触发,库可以在此处插入动态 CSS。

# useMemo 钩子

缓存函数的计算结果,只有当依赖项发生变化时,才会重新计算

# useCallback 钩子

缓存函数的定义,接收的参数是:缓存函数和依赖项,只有当依赖项 (栈值) 发生变化时,才会更新

function ProductPage({ productId, referrer, theme }) {
  // 在多次渲染中缓存函数
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]); // 只要这些依赖没有改变
  return (
    <div className={theme}>
      {/* ShippingForm 就会收到同样的 props 并且跳过重新渲染 */}
      <ShippingForm onSubmit={handleSubmit} />
    </div>
  );
}

useCallback 是由 useMemo 封装而来: (useCallback 内部存储的不是原来的函数体,而是一个普通函数返回函数体被 useMemo 缓存的结果)

function useCallback (fn,dependencies){
  return useMemo(()=>fn,dependencies)
}

# useTransition 钩子

允许将状态转换标记为非阻塞,并允许其他更新中断它。为了更好地控制组件更新和动画而设计

# useDeferredValue 钩子

允许延迟更新 UI 的非关键部分,以让其他部分先更新。

# Fragment 组件

<Fragment> 通常使用 <>...</> 代替,它们都允许你在不添加额外节点的情况下将子元素组合。

# Profiler 组件

<Profiler> 允许你编程式测量 React 树的渲染性能。接受一个 id 用于表示测量的 UI 部分,接受一个回调函数,当包裹的组件树更新时会传入渲染信息进行调用。

<Profiler id="App" onRender={onRender}>
  <App />
</Profiler>

# StrictMode 组件

开启严格模式,开发阶段会渲染两次,使得尽早地发现组件中错误

# Suspense 组件

展示子组件加载完成前渲染的内容.

<Suspense fallback={<Loading />}>
  <AsyncComponent />
</Suspense>

# memo 方法

memo 允许你的组件在道具没有改变的情况下跳过重新渲染

# createPortal 方法

createPortal 允许你将 JSX 作为 children 渲染至 DOM 的指定部分。

# createRoot 方法

createRoot 允许在浏览器的 DOM 节点中创建根节点以显示 React 组件。

# hydrateRoot 方法

hydrateRoot 函数允许你在先前由 react-dom/server 生成的浏览器 HTML DOM 节点中展示 React 组件。

# act 方法

行为测试助手,用于测试

# forwardRef 方法

允许组件使用 ref 将 DOM 节点指向给父组件。

# lazy 方法

延迟加载组件 (懒加载)。常配合 Suspense 组件使用

# startTransition 方法

可以在不阻止 UI 的情况下更新状态。

# Vue 和 React 比对

# setup 和 Hooks

  • React Hooks 在组件每次更新时,如果不做优化就会重新调用,这也带来一些性能问题
  • Hooks 有严格的调用顺序,并且不能写在条件分支中,还必须要写在 react 组件里面
  • 昂贵的计算需要使用 useMemo, 也需要传入正确的数组
  • 要解决变量闭包导致的问题,再结合并发功能,使得很难推理出一段钩子代码是什么时候运行的,并且很不好处理需要在多次渲染间保持引用的可变状态

# react 类组件和函数式组件

类组件和函数式的比对实际上是:面向对象和函数式编程这两大编程思想的碰撞
函数式编程关心的是:需要做什么,而不是怎么去做,而面向对象关心的是:数据和对象

# 面向对象编程

完成某项任务关心的是:数据和对象
面向对象编程主要围绕着数据或者对象而不是功能和逻辑实现,他将 关注点放在对于数据的操作方法面向对象将数据和操作方法封装为一个类中,这样有利于代码的可复用性和可扩展性
面向对象编程的优点是:效率高 (符合现实世界)、容易维护 (结构清晰)、易扩展 (面向对象的程序往往高内聚而低耦合)、可重用 (得益于对象的继承)
面型对象编程的缺点是:过度的对象化、状态过于共享导致推理复杂、状态共享导致的并发问题 (可变状态复杂的共享机制导致面向对象的代码几乎不可能并行化,需要复杂的线程锁定、互斥等机制)、消耗内存、性能低 (会创建很多的类和实例)

# 面向对象编程三大特点:

  • 封装
    • 封装意味着所有的数据和方法都被封装在对象内 ,由开发者自己选择性的去公开哪些属性和方法,对于创建的实例来说他能访问的只能是这些公开的属性和方法,而对于其他对象来说是无权访问此类或者进行更改, 封装这一特性为程序提供了更高的安全性
  • 继承
    • 继承意味着代码的可重用性 ,子类和父类这两个概念就是很好的体现,子类拥有父类所有的属性和方法避免数据和方法的重复定义,同时也能够保持独特的层析结构, 继承这一特性为程序提供了可重用性
  • 多态
    • 多态意味着设计对象以共享行为,使用继承子类可以用新的方法去覆盖父类共享的行为,多态性允许同一个方法执行两种不同的行为:覆盖和重载。

# 函数式编程

完成某项任务关心的是:需要做什么,而不是怎么去做
函数式编程又称声明式编程,最明显的特点就是我们 不太关心函数的具体实现 ,而 只关心自己的业务逻辑线路
函数式编程的优点是:代码可读性强、有一定的逻辑复用能力、并发速度快、出错率少易排查;
函数式编程的缺点是:性能消耗大 (主要是创建执行上下文的消耗) 和 资源占用大 (数据不可变导致要创建很多重复的对象), 同时不利于实现时间旅行等操作 (状态很难回滚)

# 函数式编程三大特点:

  • 函数是一等公民:在 JS 中函数和其他数据类型一样处于平等地位,可以作为变量赋值给其他变量,并且可以作为参数和返回值
  • 声明式编程:函数式编程又称声明式编程,我们不太关心函数内部的具体实现,而是关心业务逻辑的执行流程
  • 纯函数:纯函数特点:无副作用、引用透明 和 数据不可变
    • 无副作用:本身不会依赖和修改外部变量
    • 引用透明:输入相同的值一定会得到相同的结果
    • 数据不可变:针对引用数据类型的入参,最好的方式是重新生成一份数据
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

dmq 微信支付

微信支付

dmq 支付宝

支付宝