- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
在 Vue 3 应用中集成 Twitch、YouTube 等第三方 SDK,或在运行时按需加载一段远程 JavaScript,是常见的真实需求。VueUse 提供的useScriptTag组合式函数正是为此而生:它负责在组件挂载时创建并注入<script>标签、在组件卸载时自动清理,并且能智能复用相同 URL 的脚本,避免重复请求。读完本文,你将掌握useScriptTag的全部配置项、手动加载模式,以及它内部基于 DOM 查询、事件监听与生命周期钩子的完整实现原理,能够直接在自己的项目中正确使用并排查问题。
功能概述
useScriptTag的核心职责可以概括为三点:
- 按需注入:给定一个脚本 URL,在组件挂载时自动创建
<script>标签并追加到document.head; - 自动清理:组件卸载时自动把该脚本标签从 DOM 中移除,防止脚本与事件监听器长期驻留;
- URL 去重:如果页面中已经存在相同
src的脚本标签,不会重复创建,而是直接复用它。
这一行为与其文档描述完全一致:脚本在组件挂载时自动加载、卸载时自动删除;同时需要注意,如果同页面上此前某次调用已经加载过同一个 URL 并随后卸载了该脚本,那么再次调用时脚本会重新加载,因为旧的标签已被移除(见 index.md)。
基础用法:自动加载模式
默认情况下,useScriptTag只接受一个脚本 URL 和一个加载完成的回调,组件挂载后立即加载脚本:
import { useScriptTag } from '@vueuse/core' useScriptTag( 'https://player.twitch.tv/js/embed/v1.js', // 脚本加载完成后触发,回调参数为已就绪的 script 元素 (el: HTMLScriptElement) => { // do something }, )回调中的el就是真实存在 DOM 中的HTMLScriptElement,你可以在这里初始化第三方 SDK、读取脚本暴露的全局对象,或执行后续业务逻辑。
useScriptTag从 packages/core/index.ts 中统一导出(export * from './useScriptTag'),因此它和其他所有核心工具一样,可以直接从@vueuse/core包中按需引入。该包要求 Vue^3.5.0作为 peerDependency(见 packages/core/package.json)。
手动模式:精确控制加载与卸载时机
如果不想在挂载时就加载脚本,而是希望在某个用户操作、路由跳转或业务条件成立时才加载,可以把manual: true传给配置项。此时useScriptTag返回load与unload两个控制函数,供你自行决定时机:
import { useScriptTag } from '@vueuse/core' const { scriptTag, load, unload } = useScriptTag( 'https://player.twitch.tv/js/embed/v1.js', () => { // do something }, { manual: true }, ) // 手动控制 await load() await unload()load返回一个 Promise,因此可以配合await在脚本真正就绪后再继续后续逻辑;unload则同步移除脚本标签。关于两者的返回值与具体行为,在后面的源码剖析小节中会详细展开。
完整配置项一览
useScriptTag的第二个参数是onLoaded回调(可省略),第三个参数是配置对象UseScriptTagOptions。除文档明确提到的manual外,它还支持一大批实用选项,全部定义在 packages/core/useScriptTag/index.ts 的UseScriptTagOptions接口中:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
immediate | boolean | true | 是否在组件挂载后立即加载脚本。设为false时即使不开启manual,也需要手动调用load()才会加载 |
async | boolean | true | 是否给<script>添加async属性 |
type | string | 'text/javascript' | 脚本的type属性 |
manual | boolean | false | 是否完全手动控制加载与卸载时机 |
crossOrigin | 'anonymous' \| 'use-credentials' | — | 设置脚本的crossorigin属性,用于 CORS 场景 |
referrerPolicy | 'no-referrer' \| 'no-referrer-when-downgrade' \| 'origin' \| 'origin-when-cross-origin' \| 'same-origin' \| 'strict-origin' \| 'strict-origin-when-cross-origin' \| 'unsafe-url' | — | 设置脚本的referrerpolicy属性,控制请求携带的 Referrer 信息 |
noModule | boolean | — | 是否添加nomodule属性,常用于为不支持模块的旧浏览器加载降级脚本 |
defer | boolean | — | 是否添加defer属性,让脚本在文档解析完成后执行 |
attrs | Record<string, string> | {} | 以对象形式追加任意自定义属性(如id、data-*等) |
nonce | string | undefined | CSP(内容安全策略)所需的 nonce 值 |
document | Document | 默认 document | 自定义 document 实例,例如处理 iframe 或测试环境 |
其中document选项继承自 packages/core/_configurable.ts 中定义的ConfigurableDocument接口,默认值defaultDocument仅在客户端(isClient)环境下指向window.document,这在服务端渲染时会自动退化为undefined,从而保证 SSR 安全。
结合配置项,一个带自定义属性和 CSP nonce 的完整示例大致如下:
const { load } = useScriptTag( 'https://example.com/sdk.js', () => console.log('SDK ready'), { attrs: { id: 'my-sdk', 'data-env': 'production' }, nonce: 'RANDOM_NONCE', defer: true, }, )这些选项在源码中会逐一映射到创建的<script>元素上,具体映射逻辑见下文剖析。
源码剖析:useScriptTag 是如何工作的
理解了配置项,再来看看 packages/core/useScriptTag/index.ts 内部的核心实现,这能帮你理解它为什么具备"自动加载、自动卸载、URL 去重"三大行为。
返回值结构
useScriptTag返回UseScriptTagReturn:
export interface UseScriptTagReturn { scriptTag: ShallowRef<HTMLScriptElement | null> load: (waitForScriptLoad?: boolean) => Promise<HTMLScriptElement | boolean> unload: () => void }scriptTag:一个ShallowRef,指向当前实际存在的<script>元素,卸载后变为null,可响应式地驱动 UI 状态;load:加载函数,返回 Promise,可通过参数控制等待策略(下文说明);unload:卸载函数,同步移除脚本标签。
loadScript:查询、创建与事件绑定
loadScript是内部核心函数(源码第 98 行起),其流程为:
- SSR 保护:如果
document不存在(如服务端环境),直接resolve(false),不进行任何 DOM 操作; - URL 查询去重:通过
document.querySelector('script[src="..."]')查找是否已存在相同src的脚本。若不存在,则document.createElement('script')创建新元素,并依次设置type、async、src,再按配置可选地设置defer、crossOrigin、noModule、referrerPolicy、nonce,最后通过el.setAttribute把attrs中的自定义属性全部写入;若已存在且带有data-loaded标记,则直接复用并 resolve; - 事件监听:用
useEventListener为脚本元素绑定error、abort(两者都 reject)与load(设置data-loaded标记、调用onLoaded回调并 resolve)事件,监听选项为{ passive: true }; - 追加到 head:
document.head.appendChild(el)注入页面。
其中"查找已有脚本并复用"正是文档中"相同 URL 不重复创建"承诺的实现依据;而data-loaded属性标记则保证了脚本加载完成后,后续复用同一 URL 的调用能立即拿到已就绪的元素。
load:单例 Promise 防重复调用
load是对loadScript的单例包装:内部缓存_promise,如果已有进行中的加载请求,直接返回同一个 Promise,避免对同一脚本重复触发加载。load(waitForScriptLoad = true)的布尔参数含义是:true时 Promise 等到脚本触发load事件后才 resolve;false时脚本追加到 DOM 后立即 resolve,不等待真实加载完成。
unload:移除脚本并重置状态
unload会先将_promise置空、把scriptTag.value设为null,再通过querySelector找到对应src的脚本元素并从document.head移除。这也解释了文档中的提醒:如果你在卸载后又用同一 URL 调用useScriptTag,由于旧标签已被删除,脚本会重新加载一次。
生命周期绑定
源码末尾通过tryOnMounted与tryOnUnmounted(来自@vueuse/shared,见 packages/shared/tryOnMounted/index.ts)把加载与清理挂到组件生命周期上:
immediate && !manual时,挂载后调用load;!manual时,卸载时调用unload。
值得注意的是tryOnMounted的语义:如果在组件生命周期内调用则挂载到onMounted,否则(如非组件上下文)直接同步执行,这让useScriptTag在普通模块或测试环境中也能工作。
测试验证:行为都有据可查
仓库为useScriptTag提供了完整的浏览器环境测试 packages/core/useScriptTag/index.browser.test.ts,可以作为理解其行为的"行为规范":
- 自动加载:
immediate: true时,组件挂载后document.head.appendChild被调用,且document.head中出现对应src的HTMLScriptElement; - URL 复用:同一 URL 分别创建两个
manual: true的实例并各自load,appendChild只被调用一次,两个实例共享同一个脚本元素; - 自定义属性:传入
attrs: { 'id': ..., 'data-test': ... }后,创建的元素上能读取到对应属性; - 卸载清理:组件
unmount后removeChild被调用、脚本从 DOM 消失、scriptTag变为null; - 手动 unload:调用
unload同样能移除脚本; - 多实例卸载:两个共享同一 URL 的实例分别
unload时,removeChild也只被调用一次(幂等清理)。
这些测试用例直接印证了本文前文所述的"自动加载 / URL 去重 / 自动卸载"三大行为,也为你排查"脚本没加载"、"脚本没移除"等问题提供了参照。
最佳实践与注意事项
- 重复加载提醒:
useScriptTag只保证"同一时刻"不重复创建同一 URL 的脚本。如果某次调用加载后又卸载了该脚本,下次调用会重新发起加载——这正是文档强调的注意事项,适合用manual模式配合全局状态统一管理; - 等待语义:默认
load()会等待脚本真正加载完成(load事件),适合"先加载 SDK 再初始化"的时序依赖场景;若只需"尽快把脚本放进 DOM",可传load(false)立即返回; - SSR 安全:服务端没有
document,此时loadScript直接 resolvefalse而不会抛错,配合immediate也不会在服务端注入脚本,可安全用于 Nuxt 等 SSR 框架; - 自定义 document:在 iframe 或特殊测试环境中,可通过
document选项注入自定义 Document,避免影响主文档; - CSP 环境:受内容安全策略约束的站点,应使用
nonce选项传入与 CSP 策略匹配的 nonce 值; - 响应式 src:
src参数的类型是MaybeRefOrGetter<string>,即支持传入 ref 或 getter,让脚本地址本身也可以是响应式的。
总而言之,useScriptTag用极小的 API 面(一个 URL、一个回调、一组配置项)封装了"动态脚本注入 + 生命周期管理 + 去重复用"这一完整能力,其文档、源码与测试三者在仓库中相互印证,是理解 VueUse"小而美"设计哲学的典型示例。
- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
相关推荐
Middleman中的第三方脚本管理:优化第三方资源加载
Middleman中的第三方脚本管理:优化第三方资源加载 在现代Web开发中,第三方脚本(如分析工具、广告SDK、社交分享按钮)已成为页面不可或缺的组成部分。然
前端开发工具VueUse useScriptTag 深度解析:在 Airi 中以声明式方式管理动态 Script 标签
VueUse useScriptTag 深度解析:在 Airi 中以声明式方式管理动态 Script 标签 本文以 Airi 仓库 .agents/skills
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染在 Vue 3 与 AI 桌面应用中优雅加载异步状态:useAsyncState 完整实战指南
在 Vue 3 与 AI 桌面应用中优雅加载异步状态:useAsyncState 完整实战指南 useAsyncState 是 VueUse 提供的一个 Sta
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考