跳转到内容

Custom Hook 与 Composable

两个 Demo 都复用“监听窗口尺寸”的逻辑。调整窗口宽度时都会更新,但复用函数参与组件生命周期的方式不同。

React

组件 render 时按固定顺序调用 Custom Hook。

在线预览

useViewportSize()

窄屏

窗口宽度:等待客户端挂载

窗口高度:等待客户端挂载

调整浏览器窗口后,Hook 内部的 resize 监听会更新组件 state。

组件关系与数据流

Reusable StateConsumerHook / ComposableBrowser API

差异焦点Custom Hook 必须遵守 Hooks 调用规则,它不是独立运行的组件实例。

实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。

flowchart TB
  accTitle: React Custom Hook 运行机制
  accDescr: ReactDemo 每次 render 都按固定顺序调用 useViewportSize,Hook 的 effect 管理窗口监听,resize 时 setter 更新 Hook state 并重新执行组件。
  demo["ReactDemo render"] --> hookLogic{{"调用 Custom Hook"}}
  hookLogic --> effect{{"useEffect 注册与清理"}}
  effect --> windowResource[("window resize")]
  windowResource -.->|"setSize"| state(["size state"])
  state -->|"调度下一次 render"| demo
  class state state
  class demo component
  class hookLogic,effect process
  class windowResource resource
  classDef state fill:#ffffff,stroke:#149eca,color:#172033,stroke-width:1.2px
  classDef component fill:#e5f6fb,stroke:#149eca,color:#172033,stroke-width:2px,font-weight:600
  classDef process fill:#f4fbfd,stroke:#149eca,color:#172033,stroke-width:1.6px
  classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4px

Hook 随组件 render 调用,通过 Hook 顺序关联保存的 state 和 effect。

React 流程图
React 组件关系与数据流

滚轮缩放 · 按住拖动 · 双击重置 · Esc 关闭

实际运行源码

import { useViewportSize } from "./useViewportSize";
export function ReactDemo() {
const { height, label, width } = useViewportSize();
return (
<div className="demo-stack">
<div className="demo-card">
<span className="demo-tag">useViewportSize()</span>
<h2>{label}</h2>
<p>窗口宽度:{width || "等待客户端挂载"}</p>
<p>窗口高度:{height || "等待客户端挂载"}</p>
</div>
<p className="demo-muted">
调整浏览器窗口后,Hook 内部的 resize 监听会更新组件 state。
</p>
</div>
);
}
Vue 3

setup 调用 Composable 并获得可复用的响应式 refs。

在线预览

useViewportSize()

窄屏

窗口宽度:等待客户端挂载

窗口高度:等待客户端挂载

调整浏览器窗口后,Composable 内部的 resize 监听会修改 refs。

组件关系与数据流

Reusable StateConsumerHook / ComposableBrowser API

差异焦点普通组合函数可以使用控制流组织逻辑,依赖通过响应式引用连接。

实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。

flowchart TB
  accTitle: Vue Composable 运行机制
  accDescr: VueDemo setup 调用 useViewportSize 一次,Composable 创建 refs 并登记挂载与卸载回调,resize 时直接修改 refs。
  demo["VueDemo setup"] --> composable{{"调用 Composable 一次"}}
  composable --> lifecycle{{"登记 onMounted / onUnmounted"}}
  lifecycle --> windowResource[("window resize")]
  windowResource -.->|"直接修改 refs"| state(["width / height refs"])
  state -->|"响应式更新"| template["VueDemo template"]
  class state state
  class demo,template component
  class composable,lifecycle process
  class windowResource resource
  classDef state fill:#ffffff,stroke:#42b883,color:#172033,stroke-width:1.2px
  classDef component fill:#e7f8f0,stroke:#42b883,color:#172033,stroke-width:2px,font-weight:600
  classDef process fill:#f2fbf7,stroke:#42b883,color:#172033,stroke-width:1.6px
  classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4px

Composable 在 setup 中创建并返回 refs,生命周期钩子跟随调用它的组件。

Vue 3 流程图
Vue 3 组件关系与数据流

滚轮缩放 · 按住拖动 · 双击重置 · Esc 关闭

实际运行源码

<script setup lang="ts">
import { useViewportSize as viewportSizeComposable } from "./useViewportSize.vue";
const { height, label, width } = viewportSizeComposable();
</script>
<template>
<div class="demo-stack">
<div class="demo-card">
<span class="demo-tag">useViewportSize()</span>
<h2>{{ label }}</h2>
<p>窗口宽度:{{ width || "等待客户端挂载" }}</p>
<p>窗口高度:{{ height || "等待客户端挂载" }}</p>
</div>
<p class="demo-muted">
调整浏览器窗口后,Composable 内部的 resize 监听会修改 refs。
</p>
</div>
</template>

本站同时运行 React Fast Refresh 与 Vue HMR,因此 Vue Demo 使用中性别名调用 composable,避免开发环境把 use* 调用误判成 React Hook;普通 Vue 项目可以直接调用 useViewportSize()