通过 100+ 条技巧学习 Nuxt!

storyblok
@storyblok/nuxt

Storyblok Nuxt 模块
Storyblok Logo

@storyblok/nuxt

用于 Storyblok 无头 CMS 的 Nuxt 3 模块。


Storyblok JS Client npm

Follow @Storyblok
Follow @Storyblok

快速开始一个新项目

您是否渴望开始编码?按照这些步骤使用 Storyblok 和 Nuxt 快速开始一个新项目,并在几分钟内开始使用!

终极教程

您是否正在寻找一个实践性强、循序渐进的教程?Nuxt 终极教程 涵盖了您所需的一切!它提供了关于使用 Storyblok 和 Nuxt 从头到尾构建一个完整的、多语言网站的全面指导。

安装

安装 @storyblok/nuxt

npx nuxi@latest module add storyblok

将以下代码添加到 nuxt.config.js 的模块部分,并将 accessToken 替换为 Storyblok 空间的 API 令牌。

import { defineNuxtConfig } from 'nuxt';

export default defineNuxtConfig({
  modules: [
    ['@storyblok/nuxt', { accessToken: '<your-access-token>' }]
    // ...
  ]
});

如果您愿意,也可以使用 storyblok 配置

import { defineNuxtConfig } from 'nuxt';

export default defineNuxtConfig({
  modules: ['@storyblok/nuxt'],
  storyblok: {
    accessToken: '<your-access-token>'
  }
});

警告 此 SDK 在底层使用了 Fetch API。如果您的环境不支持它,您需要安装一个 polyfill,例如 isomorphic-fetch。有关 storyblok-js-client 文档的更多信息。

选项

当您初始化模块时,您可以传递所有 @storyblok/vue 选项,以及 bridge 选项(在我们的 JS SDK Storyblok bridge 部分中解释)和一个 enableSudoMode 选项来定义您自己的插件(见下文)。

注意 如果您想在 nuxt-devtools 中使用 Storyblok,您可以使用 devtools 选项。如果启用此选项,请确保已安装 @nuxt/devtools 模块并在您的 nuxt 配置中启用它。

// Defaults
["@storyblok/nuxt", {
  {
    accessToken: "<your-access-token>",
    bridge: true,
    devtools: true,
    apiOptions: {}, // storyblok-js-client options
  }
}]

定义您自己的插件

虽然推荐的方法涵盖了大多数情况,但在某些特定情况下,您可能需要使用 enableSudoMode 选项并禁用我们的插件,以便您可以整合您自己的插件。

// nuxt.config.ts
modules: [
  [
    '@storyblok/nuxt',
    {
      accessToken: '<your-access-token>',
      enableSudoMode: true
    }
  ]
];

为了在 SDK 的 apiOptions 中包含其他功能,例如自定义缓存方法,您可以在 plugins 文件夹(自动导入)中实现以下解决方案

// plugins/storyblok.js
import { apiPlugin, StoryblokVue } from '@storyblok/vue';

export default defineNuxtPlugin(({ vueApp }) => {
  vueApp.use(StoryblokVue, {
    accessToken: '<your-access-token>',
    apiOptions: {
      cache: {
        type: 'custom',
        custom: {
          flush() {
            console.log('all right');
          }
        }
      }
    },
    use: [apiPlugin]
  });
});

区域参数

可能的值

  • eu (默认): 适用于在欧盟创建的空间
  • us: 适用于在美国创建的空间
  • ap: 适用于在澳大利亚创建的空间
  • ca: 适用于在加拿大创建的空间
  • cn: 适用于在中国创建的空间

在美国创建的空间的完整示例

["@storyblok/nuxt", {
  {
    accessToken: "<your-access-token>",
    apiOptions: {
      region: "us"
    }
  }
}]

重要 对于在美国或中国创建的空间,必须指定 region 参数。

开始使用

1. 创建组件并将它们链接到 Storyblok 可视化编辑器

要将您的 Vue 组件链接到 Storyblok 空间中的等效组件

  • 首先,您需要将它们全局加载,将它们添加到 ~/storyblok 目录。重要的是在您的代码中使用 PascalCase 命名它们 ExampleComponent.vue,并在您的 Storyblok 空间中使用连字符 example-component,以便它们将被自动导入。
    如果您想为 Storyblok 相关组件定义自己的目录,您可以使用 nuxt.config.js 中的 componentsDir 选项
    // nuxt.config.ts
    modules: [
      [
        "@storyblok/nuxt",
        {
          accessToken: "<your-access-token>",
          componentsDir: '~/components',
        }
      ]
    ],
    components: {
      dirs: [
        {
          path: '~/components/storyblok',
          global: true,
        }
      ]
    },
    

    否则,您可以设置另一个目录并手动加载它们(例如,通过使用 Nuxt 插件)。

    警告 请注意,如果您在 storyblok 文件夹中将一个组件命名为与 components 文件夹中的另一个组件相同,它将无法正常工作。提示:保持您的 Nuxt 项目中的组件名称不同。

  • 对于每个组件,在其根元素上使用 v-editable 指令,传递它们接收的 blok 属性
<div v-editable="blok"></div>
  • 最后,使用 <StoryblokComponent>,它在 Nuxt 应用程序中全局可用
<StoryblokComponent :blok="blok" />

blok 是来自 Storblok 的内容交付 API 的实际 blok 数据。

2. 获取 Storyblok Stories 并监听可视化编辑器事件

Composition API

最简单的方法是使用 useAsyncStoryblok 一行代码的组合式函数(它是自动导入的)。您需要将 slug 作为第一个参数传递,而第二个和第三个参数 apiOptionsbridgeOptions 分别是可选的。

在我们的 API 文档中查看可用的 apiOptions,以及传递给 Storyblok Bridge 的 bridgeOptions

注意 如果您想了解更多关于版本控制 { version: "draft" /* 或 "publish" */ } 的信息,请转到 使用预览和/或生产环境 部分

<script setup>
  const story = await useAsyncStoryblok(
    "vue",
    { version: "draft", resolve_relations: "Article.author" }, // API Options
    { resolveRelations: ["Article.author"], resolveLinks: "url" } // Bridge Options
  );

  if (story.value.status) {
    throw createError({
      statusCode: story.value.status,
      statusMessage: story.value.response
    });
  }
</script>

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>

这是在 useStateuseStoryblokBridge 函数中分别使用 useStoryblokApi 的简写等效方法

<script setup>
  const story = useState();
  const storyblokApi = useStoryblokApi();

  const { data } = await storyblokApi.get(
    `cdn/stories/vue`,
    {
      version: "draft"
    }
  );
  story.value = data.story;

  onMounted(() => {
    useStoryblokBridge(
      story.value.id,
      (evStory) => (story.value = evStory),
      { resolveRelations: ["Article.author"], resolveLinks: "url" } // Bridge Options
    );
  });
</script>

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>

useState 是一个 SSR 友好的 ref 替代品。它的值将在服务器端渲染后(在客户端 hydration 期间)被保留。

渲染富文本

您可以使用 StoryblokRichText 组件渲染富文本字段

<template>
  <StoryblokRichText :doc="blok.articleContent" />
</template>

或者您可以使用 useStoryblokRichText 组合式函数来获得更多控制

<script setup>
  const { render } = useStoryblokRichText({
    // options like resolvers
  })

  const root = () => render(blok.articleContent);
</script>

<template>
  <root />
</template>

有关可以传递给 useStoryblokRichText 的更多强大选项,请查阅 完整选项 文档。

覆盖默认解析器

您可以通过将 resolver 属性传递给 StoryblokRichText 组件来覆盖默认解析器,例如,使用 vue-router 链接或添加自定义 codeblok 组件:

<script setup>
  import { NuxtLink } from '#components';
  import type { StoryblokRichTextNode } from '@storyblok/vue';
  import CodeBlok from "./components/CodeBlok.vue";

  const resolvers = {
    // NuxtLink example:
    [MarkTypes.LINK]: (node: StoryblokRichTextNode<VNode>) =>
      h(NuxtLink, {
        to: node.attrs?.href,
        target: node.attrs?.target,
      }, node.text),
    // Custom code block component example:
    [BlockTypes.CODE_BLOCK]: (node: Node) => {
      return h(CodeBlock, {
        class: node?.attrs?.class,
      }, node.children)
    },
  }
</script>

<template>
  <StoryblokRichText :doc="blok.articleContent" :resolvers="resolvers" />
</template>

如果您想使用 useStoryblokRichText 组合式函数,您可以通过 options 对象传递 resolvers

<script setup>
  import CodeBlok from "./components/CodeBlok.vue";

  const { render } = useStoryblokRichText({
    resolvers: {
      // NuxtLink example:
      [MarkTypes.LINK]: (node: StoryblokRichTextNode<VNode>) =>
        h(NuxtLink, {
          to: node.attrs?.href,
          target: node.attrs?.target,
        }, node.text),
      // Custom code block component example:
      [BlockTypes.CODE_BLOCK]: (node: Node) => 
        h(CodeBlock, {
          class: node?.attrs?.class,
        }, node.children)
    }
  });

  const root = () => render(blok.articleContent);
</script>

旧版渲染富文本

!警告
旧版 richTextResolver 即将弃用。我们建议迁移到上面描述的新方法。

您可以通过使用 @storyblok/nuxt 附带的 renderRichText 函数和一个 Vue 计算属性来轻松渲染富文本

<template>
  <div v-html="articleContent"></div>
</template>

<script setup>
  const props = defineProps({ blok: Object });
  const articleContent = computed(() => renderRichText(props.blok.articleContent));
</script>

您还可以通过将选项作为 renderRichText 函数的第二个参数来设置自定义 Schema 和组件解析器

<script setup>
  import cloneDeep from 'clone-deep';

  const mySchema = cloneDeep(RichTextSchema); // you can make a copy of the default RichTextSchema
  // ... and edit the nodes and marks, or add your own.
  // Check the base RichTextSchema source here https://github.com/storyblok/storyblok-js-client/blob/v4/source/schema.js

  const props = defineProps({ blok: Object });

  const articleContent = computed(() =>
    renderRichText(props.blok.articleContent, {
      schema: mySchema,
      resolver: (component, blok) => {
        switch (component) {
          case 'my-custom-component':
            return `<div class="my-component-class">${blok.text}</div>`;
          default:
            return 'Resolver not defined';
        }
      },
    }),
  );
</script>

3. 使用预览和/或生产环境

请记住,bridge 仅在使用 version: 'draft'预览访问令牌时有效。

对于生产站点,不作为内容编辑器的预览使用,应使用 version: 'published'公共访问令牌

注意 如果您将生产环境用作营销人员和您的公共站点的预览,您将需要一个插件来处理不同的 .env 变量,或者使用预览访问令牌的版本,检查您是否在 Storyblok 内部。例如,类似 if (window.location.search.includes(_storyblok_tk[token]=<YOUR_TOKEN>) 的代码。

查看关于如何访问不同内容版本的官方文档。

使用 Nuxt 处理不同内容版本的推荐方法是将环境变量与 Nuxt 运行时配置结合使用,以在您的应用程序中公开配置和密钥

在您的 nuxt.config.ts

export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      storyblokVersion: process.env.STORYBLOK_VERSION || 'published'
    }
  }
});

然后您可以在您的组件中访问运行时配置

const config = useRuntimeConfig();

const story = await useAsyncStoryblok(
  'blog',
  {
    version: config.public.storyblokVersion,
    resolve_relations: 'overview.featured_story'
  },
  { resolveRelations: 'overview.featured_story' }
);

// or

const { data: articles } = await storyblokApi.get('cdn/stories', {
  version: config.public.storyblokVersion,
  starts_with: 'blog',
  is_startpage: false
});

API

useAsyncStoryblok(slug, apiOptions, bridgeOptions)

(推荐选项)在底层使用 useState 以帮助实现 SSR 兼容性。

查看可用的 apiOptions (传递给 storyblok-js-client) 和 bridgeOptions (传递给 Storyblok Bridge)。

useStoryblok(slug, apiOptions, bridgeOptions)

当我们需要进行完全的客户端请求时,例如获取已登录用户的个性化数据,使用 useStoryblok 而不是 useAsyncStoryblok 可能会有所帮助。

查看可用的 apiOptions (传递给 storyblok-js-client) 和 bridgeOptions (传递给 Storyblok Bridge)。

useStoryblokApi()

返回 storyblok-js-client 的实例。

useStoryblokBridge(storyId, callback, bridgeOptions)

使用此单行函数来涵盖最常见的用例:当 Storyblok 可视化编辑器上发生任何类型的更改时更新 story。

Storyblok JavaScript SDK 生态系统

A visual representation of the Storyblok JavaScript SDK Ecosystem

更多资源

支持

贡献

请查看我们的贡献指南和我们的行为准则。此项目使用 semantic-release 通过使用提交消息生成新版本,我们使用 Angular Convention 来命名提交。查看 此问题,了解 semantic-release FAQ。