ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VueUse useSSRWidth 完全指南:在 SSR 场景下统一注入视口宽度,避免水合不一致

VueUse useSSRWidth 完全指南:在 SSR 场景下统一注入视口宽度,避免水合不一致 前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载导读useSSRWidth是 VueUse 提供的一个 SSR服务端渲染辅助工具它允许你在服务端渲染期间为整个应用注入一个全局视口宽度global viewport width从而使useMediaQuery、useBreakpoints等依赖视口宽度的响应式工具在服务端也能得到确定、可预期的计算结果。读完本文你将掌握如何通过provideSSRWidth在应用级或组件级注入 SSR 宽度、如何在子组件中用useSSRWidth读取该值、以及它如何被useMediaQuery与useBreakpoints底层消费以规避水合不一致hydration mismatch。为什么需要 useSSRWidth在浏览器环境中window.matchMedia()可以随时查询真实视口宽度因此useMediaQuery和useBreakpoints的行为完全取决于用户的屏幕。但在 SSR服务端渲染过程中并不存在浏览器窗口对象服务端没有window、没有matchMedia渲染出的 HTML 只能依赖一个假设的视口宽度来决定样式类与内容结构。问题在于服务端渲染时做出的视口假设与客户端水合hydration时的真实视口宽度很可能不一致例如服务端按 768px 渲染出移动端布局客户端却是 1440px 的桌面屏。这一差异会导致水合不一致进而引发页面闪烁、警告甚至渲染异常。useSSRWidth正是为解决这个问题而生的它在服务端阶段确定一个宽度值供所有依赖宽度的组合式函数共享保证服务端与客户端首屏渲染结果一致。该工具与useMediaQuery、useBreakpoints的官方文档相互印证后者分别提供了局部ssrWidth选项并引用了provideSSRWidth做全局配置而useSSRWidth是这一机制的全局枢纽。快速上手两种注入方式按照useSSRWidth官方文档 的说明注入 SSR 宽度有两种途径应用级注入与根组件注入。方式一应用级注入推荐全局统一在创建应用实例后、mount之前调用provideSSRWidth并传入应用实例作为第二个参数import { provideSSRWidth } from vueuse/core import { createApp } from vue const app createApp(App) provideSSRWidth(500, app) app.mount(#app)此时 500 作为全局视口宽度被注入到整个应用应用中任何位置的useMediaQuery、useBreakpoints在 SSR 阶段都会按照 500px 视口来解析。方式二根组件注入组件作用域在根组件的script setup中直接调用不传应用实例script setup langts import { provideSSRWidth } from vueuse/core provideSSRWidth(500) /script这种方式依赖组件树的原生 provide/inject 机制注入值对根组件及其所有后代组件可见。读取注入值useSSRWidth如果你需要在某个子组件中拿到这个被注入的宽度值例如做额外的逻辑判断使用useSSRWidthscript setup langts import { useSSRWidth } from vueuse/core const width useSSRWidth() // width 为 number注入成功或 undefined未注入 /scriptAPI 详解与底层实现provideSSRWidth(width, app?)源码位于 useSSRWidth 实现其完整签名与逻辑如下const ssrWidthSymbol Symbol(vueuse-ssr-width) as InjectionKeynumber | null export function provideSSRWidth(width: number | null, app?: Appunknown) { if (app ! undefined) { app.provide(ssrWidthSymbol, width) } else { provideLocal(ssrWidthSymbol, width) } }width要注入的 SSR 视口宽度像素类型为number | null传入null表示显式清除/不启用。app可选的 Vue 应用实例。传入时走app.provide()注入范围为整个应用不传时走provideLocal()即组件级 provide。injection key内部使用Symbol(vueuse-ssr-width)作为注入键避免与用户自定义注入键冲突。useSSRWidth() 的取值逻辑export function useSSRWidth() { const ssrWidth hasInjectionContext() ? injectLocal(ssrWidthSymbol, null) : null return typeof ssrWidth number ? ssrWidth : undefined }通过hasInjectionContext()判断当前是否处于组件注入上下文在组件外部如纯工具函数中调用时不会触发 Vue 的注入告警直接返回undefined。在组件内部通过injectLocal读取。injectLocal见 实现与provideLocal见 实现配对使用允许在同一个组件内 provide 后立即 inject 取值这正是useSSRWidth/index.browser.test.ts中本地提供并读取测试所验证的行为。返回值注入值是number时原样返回否则返回undefined——这意味着未注入和注入 null在读取方都被归一化为undefined。值得注意的返回语义useSSRWidth()返回的是普通数字或undefined不是响应式 ref。原因在于SSR 宽度只在渲染阶段有意义服务端与客户端的水合前渲染都基于同一固定值不需要也不应该响应式变化。这与useMediaQuery、useBreakpoints返回ComputedRefboolean的行为形成对比。下游消费useMediaQuery 与 useBreakpoints 如何吃掉这个宽度useSSRWidth的价值体现在被依赖方消费时。两者的实现均优先读取全局注入值作为ssrWidth默认值。useMediaQuery 的消费方式useMediaQuery源码位于 useMediaQuery 实现关键片段export function useMediaQuery(query: MaybeRefOrGetterstring, options: ConfigurableWindow { ssrWidth?: number } {}) { const { window defaultWindow, ssrWidth useSSRWidth() } options const isSupported useSupported(() window matchMedia in window typeof window.matchMedia function) const ssrSupport shallowRef(typeof ssrWidth number) ... watchEffect(() { if (ssrSupport.value) { // Exit SSR support on mounted if window available ssrSupport.value !isSupported.value const queryStrings toValue(query).split(,) matches.value queryStrings.some((queryString) { const not queryString.includes(not all) const minWidth queryString.match(/\(\s*min-width:\s*(-?\d(?:\.\d*)?[a-z]\s*)\)/) const maxWidth queryString.match(/\(\s*max-width:\s*(-?\d(?:\.\d*)?[a-z]\s*)\)/) let res Boolean(minWidth || maxWidth) if (minWidth res) res ssrWidth! pxValue(minWidth[1]) if (maxWidth res) res ssrWidth! pxValue(maxWidth[1]) return not ? !res : res }) return } ... }) }从源码可以看出几个关键机制ssrWidth选项的默认值来自useSSRWidth()只要全局注入过宽度所有useMediaQuery调用都会自动继承无需逐个传参。正则解析宽度查询在 SSR 模式下它通过正则从查询串中提取min-width/max-width值并将注入宽度与断点值用/比较。长度单位由pxValue归一化——rem单位按 16px 换算其他单位直接取数值。水合后自动退出 SSR 模式ssrSupport.value !isSupported.value这行代码是关键——组件挂载且浏览器可用后SSR 支持即被关闭后续查询走真实的window.matchMedia实现服务端固定、客户端实时的无缝切换。支持not all取反查询串含not all时对匹配结果取反保证服务端判定与浏览器语义一致。useBreakpoints 的消费方式useBreakpoints源码位于 useBreakpoints 实现同样将useSSRWidth()作为默认值const { window defaultWindow, strategy min-width, ssrWidth useSSRWidth() } options const ssrSupport typeof ssrWidth number const mounted ssrSupport ? shallowRef(false) : { value: true } if (ssrSupport) { tryOnMounted(() mounted.value !!window) } function match(query: min | max, size: string): boolean { if (!mounted.value ssrSupport) { return query min ? ssrWidth pxValue(size) : ssrWidth pxValue(size) } if (!window) return false return window.matchMedia((${query}-width: ${size})).matches }所有非响应式判定方法isGreater、isGreaterOrEqual、isSmaller、isSmallerOrEqual、isInBetween在挂载前统一走match()用注入宽度与断点值做数值比较。响应式快捷方法如breakpoints.lg内部通过useMediaQuery生成查询因此自动继承了上面的正则解析逻辑。挂载后mounted变为true立即切换到真实的window.matchMedia与useMediaQuery的退出机制同构。简言之只要调用过一次provideSSRWidth整个应用内所有useMediaQuery与useBreakpoints在 SSR 阶段都会以该宽度作为判定基准无需逐处配置。局部覆盖按需传入 ssrWidth 选项useSSRWidth提供的是全局默认值但某些场景下你可能希望某处查询使用不同的宽度。此时可在调用处直接传入ssrWidth选项它会覆盖全局注入值因为ssrWidth useSSRWidth()只是选项的默认值显式传入即生效。useMediaQuery文档SSR 章节给出的经典示例import { useMediaQuery } from vueuse/core const isLarge useMediaQuery((min-width: 1024px), { ssrWidth: 768, // 启用 SSR 模式并按 768px 视口渲染 }) console.log(isLarge.value) // SSR 阶段恒为 false因为 768px 1024px onMounted(() { // 水合后按真实视口计算屏幕 1024px 为 false否则为 true console.log(isLarge.value) })useBreakpoints的用法完全一致见 其文档的 SSR 章节import { breakpointsTailwind, useBreakpoints } from vueue/core // 修正为 import { breakpointsTailwind, useBreakpoints } from vueuse/core const breakpoints useBreakpoints(breakpointsTailwind, { ssrWidth: 768, // 启用 SSR 模式并按 768px 视口渲染 })覆盖优先级可概括为调用处显式ssrWidth选项 全局provideSSRWidth注入 未配置返回 undefinedSSR 模式关闭。在服务端渲染期间未提供任何宽度时useMediaQuery/useBreakpoints会退化为不支持状态isSupported为 false 时直接返回因此生产环境强烈建议二选一进行配置。测试验证与行为保证useSSRWidth的行为由浏览器测试用例 useSSRWidth/index.browser.test.ts 明确锁定四个用例覆盖了全部核心语义默认返回 undefined未注入任何宽度时useSSRWidth()返回undefined即 SSR 宽度机制默认关闭。应用级注入provideSSRWidth(500, app)之后在app.runWithContext()上下文中useSSRWidth()得到500验证了app.provide路径。组件级注入后代读取父组件provideSSRWidth(700)后子组件渲染出700验证了组件树 provide/inject 路径。本地注入同组件读取同一组件内provideSSRWidth(800)后直接useSSRWidth()得到800验证了provideLocal/injectLocal的同一组件内自提供自读取特性。这四个用例分别对应provideSSRWidth的两种注入路径与useSSRWidth的两种读取场景是理解该 API 行为边界最直接的参考。最佳实践小结全局统一场景在应用入口调用一次provideSSRWidth(width, app)全应用的视口敏感工具自动获得一致的 SSR 基准避免逐处传参。局部差异场景个别查询需要不同宽度时用调用处ssrWidth选项覆盖全局值。Nuxt 场景useMediaQuery与useBreakpoints的文档都明确指出该模式适用于启用 SSR 的 Nuxt 项目建议在 Nuxt 插件或根组件中注入全局宽度。宽度值选择通常取你目标设备区间的代表值如 768、1024、1280并确保与客户端真实视口的主要分布一致以最大程度降低水合差异。只读语义useSSRWidth返回的是普通数值而非 ref不要在代码中假设它可以响应式更新。相关资源useSSRWidth 官方文档useSSRWidth 源码实现useSSRWidth 浏览器测试用例useMediaQuery 文档与实现、useMediaQuery 源码useBreakpoints 文档与实现、useBreakpoints 源码injectLocal 实现、provideLocal 实现pxValue 单位换算工具赞分享前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载相关推荐Airi 项目 SSR 视口宽度统一管理VueUse useSSRWidth / provideSSRWidth 实战指南Airi 项目 SSR 视口宽度统一管理VueUse useSSRWidth / provideSSRWidth 实战指南 在开启 SSR服务端渲染的 VAI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染Quasar SSR 客户端水合Client Side Hydration完全指南原理、常见不一致与排查实战Quasar SSR 客户端水合Client Side Hydration完全指南原理、常见不一致与排查实战 本文基于 quasar/app vite前端UI组件跨平台VueUse useMediaQuery 深度指南响应式媒体查询与 SSR 水合安全实践VueUse useMediaQuery 深度指南响应式媒体查询与 SSR 水合安全实践 导读 useMediaQuery 是 VueUse 中用于将原生 C前端上一篇Slidev 核心语法与交互特性实战详解逐页解剖 cypress/fixtures/basic/slides.md 端到端演示下一篇3分钟解决Git非快进错误团队协作必备的冲突处理指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表