通过 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 的 modules 部分,并将 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。如果您的环境不支持,您需要安装一个像 isomorphic-fetch 这样的 polyfill。更多信息请查看 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/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 目录。重要的是在您的代码中使用 Pascal 命名法 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>
  • 最后,使用在 Nuxt 应用中全局可用的 <StoryblokComponent>
<StoryblokComponent :blok="blok" />

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

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

组合式 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 替代品。它的值将在服务器端渲染之后(在客户端水合期间)保留。

渲染富文本

您可以使用 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. 处理预览和/或生产环境

请记住,桥接仅在使用 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 常见问题解答。