ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

airi 项目实战:用 VueUse useTextSelection 在 Vue 3 中响应式追踪用户文本选区

2026/9/10 17:54:52 拓冰建站 浏览量
airi 项目实战:用 VueUse useTextSelection 在 Vue 3 中响应式追踪用户文本选区 airi 项目实战用 VueUse useTextSelection 在 Vue 3 中响应式追踪用户文本选区【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi导读useTextSelection是 VueUse 库 Sensors传感器分类下的一个组合式函数它基于浏览器原生的Window.getSelection、stage-web中追踪选区是选中即翻译/解释浮动操作条复制时暂停自动滚动等交互的地基。阅读本文后你将掌握useTextSelection的返回值语义、类型声明、配置项以及如何在 Vue 3 组件中结合computed/watch落地真实的选区驱动交互。选区追踪的底层Window.getSelection在浏览器中用户的文本选区由Selection对象描述它通过window.getSelection()获取并在用户按下鼠标、拖动、松开以及键盘移动光标时由selectionchange事件驱动更新。VueUse 的useTextSelection正是对这一机制的响应式封装组合式函数内部在document上监听selectionchange事件把原生Selection的快照写入 Vue 的响应式容器中从而让当前选中了什么变成可被computed、watch直接消费的状态。从本仓库的源码也能看到同样的原生模式在 use-chat-history-scroll.ts 中聊天滚动锁定逻辑通过useEventListener(selectionDocument, selectionchange, ...)配合document.getSelection()?.anchorNode判断用户是否正在较旧消息上做选区从而决定是否暂停自动滚动——这正是useTextSelection要解决的同类问题在仓库中的真实落地。安装与依赖useTextSelection属于vueuse/core只需安装该包即可使用无需额外依赖pnpm add vueuse/core在 airi 仓库中vueuse/core被广泛使用例如 packages/stage-ui/package.json 依赖vueuse/core、vueuse/motion与vueuse/shared版本通过 pnpmcatalog:统一管理apps/stage-tamagotchi、apps/stage-web、apps/stage-pocket、apps/component-calling 等应用的组件与组合式函数中也大量引用vueuse/core。因此在这些子应用中直接import { useTextSelection } from vueuse/core即可无需额外安装。基础用法useTextSelection()不接收任何必填参数调用后返回一个包含多个响应式属性的对象script setup langts import { useTextSelection } from vueuse/core const state useTextSelection() /script template p{{ state.text }}/p /template在模板中直接渲染state.text用户每次拖动鼠标选中新的文本页面上的文字都会即时更新清空选区例如点击页面空白处后state.text会恢复为空字符串。由于返回的text、rects、ranges都是ComputedRef在script setup中通过state.text.value访问模板中则自动解包。若偏好解构写法也可以const { text, rects, ranges, selection } useTextSelection()需要注意的是不要用普通const { text } useTextSelection()后直接在script中当作字符串使用——必须先取.value这与 VueUse 其他返回 ref/computed 的组合式函数保持一致。返回值详解useTextSelection的返回类型UseTextSelectionReturn定义了四个字段字段类型含义textComputedRefstring当前选中的纯文本内容无选区时为空字符串rectsComputedRefDOMRect[]选区各分段对应的矩形几何信息DOMRect[]可用于定位浮层rangesComputedRefRange[]选区的Range对象数组保留 DOM 级选区结构selectionShallowRefSelection \| null原生Selection对象本身无选区时为null它们各自的典型用途text最常用。驱动选中即翻译/解释/搜索字数统计等纯文本逻辑。rects选区可能被折行拆成多个片段每个片段对应一个DOMRect含left/top/width/height。可据此在选区附近定位浮动操作条例如选中文本后在其上方弹出复制/翻译按钮。ranges需要精细 DOM 操作时使用比如用Range的getBoundingClientRect()做自定义高亮、向选区两端插入包裹节点、或把选区序列化保存。selection需要调用原生Selection方法如removeAllRanges()清除选区、toString()获取文本时直接使用类型为ShallowRef意味着对其内部可变对象不做深层响应式代理性能开销更小。一个用rects定位浮层的示例script setup langts import { computed } from vue import { useTextSelection } from vueuse/core const { text, rects } useTextSelection() // 取第一个选区矩形的几何信息用于定位浮层 const popoverStyle computed(() { const rect rects.value[0] if (!rect) return { display: none } return { top: ${rect.top window.scrollY}px, left: ${rect.left}px, } }) /script template Teleport tobody div v-iftext classselection-popover :stylepopoverStyle 已选中{{ text }} /div /Teleport /template注意rects中的坐标是基于视口的viewport 坐标因此绝对定位时应叠加window.scrollX/window.scrollY才能与文档流坐标对齐。类型声明与选项解析参考文档给出了完整的类型声明export interface UseTextSelectionOptions extends ConfigurableWindow {} export interface UseTextSelectionReturn { text: ComputedRefstring rects: ComputedRefDOMRect[] ranges: ComputedRefRange[] selection: ShallowRefSelection | null } export declare function useTextSelection( options?: UseTextSelectionOptions, ): UseTextSelectionReturnUseTextSelectionOptions继承自ConfigurableWindow后者是 VueUse 的标准配置接口核心字段为window用于注入自定义的window对象。这在以下场景非常有用SSR服务端渲染服务端不存在window显式传入{ window: undefined }可让组合式函数安全跳过浏览器专属逻辑测试在 Vitest 等测试环境中注入jsdom提供的全局对象隔离真实浏览器状态iframe / 多窗口把焦点窗口的引用注入进去追踪指定上下文中的选区。另外函数声明带有__NO_SIDE_EFFECTS__注解表示该函数是无副作用的纯声明配合打包器如 tree-shaking 友好的 tsdown/rolldown可安全地进行死代码消除与常量折叠。响应式消费computed 与 watch 的组合模式选区状态是典型的事件驱动、声明式消费场景。推荐用computed派生展示数据用watch触发副作用script setup langts import { computed, watch } from vue import { useTextSelection } from vueuse/core const { text, selection } useTextSelection() // 派生去掉空白后是否还有内容 const hasMeaningfulSelection computed(() text.value.trim().length 0) // 副作用选区变化时触发一次回调可做翻译请求、词条查询 watch(text, (value) { if (value) translate(value) }) /scripttext是ComputedRef因此任何依赖它的computed与watch都会在选区变化时自动重新求值无需手动在selectionchange事件里写清理逻辑——这正是 VueUse 组合式函数对比手写事件监听的维护性优势也是 vueuse-functions SKILL 中优先使用组合式函数而非自造轮子建议的体现该 SKILL 将useTextSelection归类在 Sensors 分类下调用策略为 AUTO即适用场景可直接自动使用。实战场景1. 聊天应用中的选中即解释在 airi 的对话式应用如 stage-ui 中的聊天组件体系里用户往往想选中 AI 回复中的某段内容进行解释、改写或翻译。用useTextSelection只需十几行script setup langts import { watch } from vue import { useTextSelection } from vueuse/core const { text } useTextSelection() watch(text, (selected) { if (selected.trim()) console.log(用户选中了, selected) }) /script2. 选区浮动操作条复制/翻译按钮结合上一节的rects定位方案可以做出选中文本 → 选区上方弹出操作条 → 点击复制/翻译的经典交互浮层使用Teleport挂到body避免被父级overflow裁剪。3. 暂停自动滚动的选区感知对照仓库中 use-chat-history-scroll.ts 的实现思路它在selectionchange时用selection?.anchorNode判断选区锚点是否位于较旧消息上若是则暂停跟随滚动。若改用useTextSelection可简化为import { useTextSelection } from vueuse/core const { selection } useTextSelection() // selection.value?.anchorNode 指向选区起始处的 DOM 节点 // 用 closest([data-message-id]) 即可定位用户选中的是哪条消息两者是同一模式的不同写法前者直接监听原生事件后者把事件封装成响应式状态适合与既有响应式逻辑computed/watch/v-if无缝组合。边界情况与注意事项无选区状态点击空白处或按下 Escape 后text变为、selection变为null消费方需做空值判断。跨行选区多行文本的选区会被拆成多个Range与多个DOMRectrects是数组而非单个矩形做浮层定位时应遍历计算包围盒。滚动与视口坐标DOMRect基于视口页面滚动后坐标会变化若浮层定位依赖它需要叠加滚动偏移或监听滚动重新计算。SSR 环境服务端无window应通过ConfigurableWindow注入或确保只在客户端挂载后调用可配合onMounted或client-only使用。输入框内的选区SelectionAPI 主要描述页面文本节点选区input/textarea内部的选区属于HTMLInputElement.setSelectionRange管辖useTextSelection通常不覆盖这一场景可结合 VueUse 的useTextareaAutosize等其他函数分别处理。性能selection使用ShallowRef持有原生对象避免深层代理开销高频选区变化下若只关心文本优先消费text而非遍历ranges。小结useTextSelection以约一个函数调用把Window.getSelectionselectionchange的浏览器样板代码收敛为text、rects、ranges、selection四个语义清晰的响应式出口。在 airi 这类重度 Vue 3 的应用栈中它与仓库已大量采用的vueuse/core生态见 stage-ui 及各 app 的依赖清单自然衔接可快速实现选中即翻译、选区浮层、聊天滚动锁定等交互其底层监听selectionchange 读取Selection的模式也能在仓库源码use-chat-history-scroll.ts中找到可对照的印证。掌握它等于为你的 Vue 应用增加了一个零依赖成本的用户意图传感器。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考