---
title: "教程：加载 js-confetti"
description: "了解如何使用 Nuxt Scripts 模块加载 js-confetti 脚本。"
canonical_url: "https://nuxt-scripts.zhcndoc.com/docs/getting-started/confetti-tutorial"
last_updated: "2026-08-11T09:33:05.723Z"
---

本教程从 [npm](https://npmjs.com) 加载 [js-confetti](https://github.com/loonywizard/js-confetti)。您将通过代理函数调用它，并为其浏览器 API 添加类型。

这是一个 [注册脚本](/scripts)，是建立在 [useScript](/docs/api/use-script) 组合式函数之上的受支持第三方集成，允许您从 NPM 加载脚本。

[`useScriptNpm()`](/scripts/npm) 是一个基于 [`useScript()`](/docs/api/use-script) 构建的[注册脚本](/scripts)。它会加载发布到 [npm](https://www.npmjs.com/) 的、可直接在浏览器中使用的软件包文件。

大多数 npm 软件包都应放在 `package.json` 中。按需加载软件包可能需要动态导入、单独的代码块，有时还需要在构建时进行转译。

对于偶尔使用且非关键的浏览器脚本，如果它已经暴露了全局 API，`useScriptNpm()` 就很有用。如果您的应用程序在整个代码库中都会使用某个软件包，请像往常一样将其安装为依赖项。

下面的三个代码片段以不同的抽象层级加载同一个文件。

<code-group>

```ts [注册脚本 useScriptNpm]
useScriptNpm({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
})
```

```ts [useScript]
useScript('https://unpkg.com/js-confetti@0.12.0/dist/js-confetti.browser.js')
```

```ts [useHead]
useHead({
  script: [
    { src: 'https://unpkg.com/js-confetti@0.12.0/dist/js-confetti.browser.js' }
  ]
})
```

</code-group>

### 加载脚本

在组件中调用 [`useScriptNpm()`](/scripts/npm)：

```vue [app.vue]
<script setup lang="ts">
useScriptNpm({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
})
</script>
```

此时，浏览器的 Network 面板中应该会显示脚本请求。

### 解析第三方脚本 API

使用 [`use`](/docs/api/use-script#nuxtusescriptoptions) 函数告诉 Nuxt Scripts 如何解析脚本的客户端 API：

```vue [app.vue]
<script setup lang="ts">
useScriptNpm({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
  scriptOptions: {
    // 告诉 useScript 如何解析第三方脚本
    use() {
      return { JSConfetti: window.JSConfetti }
    },
  },
})
</script>
```

### 使用第三方脚本 API

`js-confetti` 库暴露了一个 `JSConfetti` 类。在脚本加载后创建一个实例，然后在后续调用中重复使用该实例。

您可以显式等待脚本加载，也可以使用[代理函数](/docs/guides/key-concepts#understanding-proxied-functions)将调用排队，直到脚本准备就绪。

<code-group>

```vue [显式加载]
<script setup lang="ts">
const { onLoaded } = useScriptNpm({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
  scriptOptions: {
    use() {
      return { JSConfetti: window.JSConfetti }
    },
  },
})
onLoaded(({ JSConfetti }) => {
  // 使用真实的 API 实例
  const confetti = new JSConfetti()
  confetti.addConfetti({ emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'] })
})
</script>
```

```vue [代理函数]
<script setup lang="ts">
const { proxy } = useScriptNpm({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
  scriptOptions: {
    use: () => typeof window.JSConfetti !== 'undefined' && new window.JSConfetti()
  }
})
onMounted(() => {
  // 在 js-confetti 准备就绪前排队
  proxy.addConfetti({ emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'] })
})
</script>
```

</code-group>

`addConfetti` 仍然没有类型，因此编辑器无法检查其参数或提供补全。

### 添加类型

向 [`useScriptNpm()`](/scripts/npm) 传入泛型，并使用相同的 API 扩展 `Window`：

```vue [app.vue]
<script setup lang="ts">
export interface JSConfettiApi {
  JSConfetti: {
    new (config?: { canvas?: HTMLCanvasElement }): {
      addConfetti: (options?: { emojis?: string[] }) => Promise<void>
    }
  }
}

declare global {
  interface Window extends JSConfettiApi {}
}

const { onLoaded } = useScriptNpm<JSConfettiApi>({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
  scriptOptions: {
    use() {
      return { JSConfetti: window.JSConfetti }
    },
  },
})
onLoaded(({ JSConfetti }) => {
  const confetti = new JSConfetti()
  // 根据 JSConfettiApi 进行类型检查
  confetti.addConfetti({ emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'] })
})
</script>
```

### 延迟加载脚本

当脚本需要等待应用程序状态、事件或计时器时，请使用 `trigger`。

请参考 [脚本触发器](/docs/guides/script-triggers) 指南了解所有可用选项。

#### 使用 ref

当 `ref` 的值变为真值时，脚本会加载。

```vue [app.vue]
<script setup lang="ts">
const shouldLoad = ref(false)
const { onLoaded } = useScriptNpm<JSConfettiApi>({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
  scriptOptions: {
    trigger: shouldLoad,
    use: () => ({ JSConfetti: window.JSConfetti }),
  },
})
onLoaded(({ JSConfetti }) => {
  const confetti = new JSConfetti()
  confetti.addConfetti({ emojis: ['🎉', '🎊', '✨'] })
})
</script>

<template>
  <button @click="shouldLoad = true">
    点击加载彩带
  </button>
</template>
```

<tip>

您也可以使用计算属性 ref 或 getter 函数：`trigger: computed(() => someCondition.value)` 或 `trigger: () => shouldLoad.value`。

</tip>

#### 使用元素事件

使用 [`useScriptTriggerElement()`](/docs/api/use-script-trigger-element) 等待元素交互。

```vue [app.vue]
<script setup lang="ts">
const mouseOverEl = ref<HTMLElement | null>(null)
const { onLoaded } = useScriptNpm<JSConfettiApi>({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
  scriptOptions: {
    trigger: useScriptTriggerElement({ trigger: 'mouseover', el: mouseOverEl }),
    use: () => ({ JSConfetti: window.JSConfetti }),
  },
})
onLoaded(({ JSConfetti }) => {
  const confetti = new JSConfetti()
  confetti.addConfetti({ emojis: ['L', 'O', 'A', 'D', 'E', 'D'] })
})
</script>

<template>
  <div ref="mouseOverEl">
    <h1>鼠标悬停在这里加载彩带</h1>
  </div>
</template>
```

### 在本地打包脚本

Nuxt Scripts 默认会静态分析 `useScriptNpm()` 文件并将其打包，然后从 `/_scripts/assets/` 提供服务。这样可以避免初始连接到软件包 CDN。

如果您希望直接从配置的 CDN 加载文件，请设置 `bundle: false`。

```vue [app.vue]
<script setup lang="ts">
useScriptNpm({
  packageName: 'js-confetti',
  file: 'dist/js-confetti.browser.js',
  version: '0.12.0',
  scriptOptions: {
    bundle: false,
  },
})
</script>
```

如果不选择退出此行为，Network 面板会显示脚本是从您的应用程序服务器加载的。有关脚本实例和代理函数的更多信息，请参阅[核心概念](/docs/guides/key-concepts)。
