透過超過 100 則秘訣的合輯學習 Nuxt!

storyblok
@storyblok/nuxt

Storyblok Nuxt 模組
Storyblok Logo

@storyblok/nuxt

適用於 Storyblok Headless 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 space 的 API token。

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 選項,以及在我們的 JS SDK Storyblok bridge 區段中說明的 bridge 選項,以及用於定義您自己的 plugin 的 enableSudoMode 選項(請參閱下方)。

注意 如果您想在 nuxt-devtools 內使用 Storyblok,您可以使用 devtools 選項。如果啟用,請確保已安裝 @nuxt/devtools 模組並在您的 nuxt config 中啟用它。

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

定義您自己的 plugin

雖然建議的方法涵蓋大多數情況,但在某些特定情況下,您可能需要使用 enableSudoMode 選項並停用我們的 plugin,以便您納入自己的 plugin。

// 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]
  });
});

Region 參數

可能的值

  • eu (預設):適用於在歐盟建立的 space
  • us:適用於在美國建立的 space
  • ap:適用於在澳洲建立的 space
  • ca:適用於在加拿大建立的 space
  • cn:適用於在中國建立的 space

針對在美國建立的 space 的完整範例

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

重要 針對在美國或中國建立的 space,**必須** 指定 region 參數。

開始使用

1. 建立您的元件並將其連結至 Storyblok Visual Editor

若要將您的 Vue 元件連結至 Storyblok space 中對應的元件

  • 首先,您需要將它們全域載入,方法是將它們新增至 ~/storyblok 目錄。重要的是在您的程式碼中使用 Pascal case 命名它們(ExampleComponent.vue),並在您的 Storyblok space 內使用連字號(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 plugin)。

    警告 請注意,如果您將 storyblok 資料夾內的元件命名為與 components 資料夾中的另一個元件相同,則它將無法正常運作。提示:讓您的 Nuxt 專案中的元件保持不同的名稱。

  • 針對每個元件,在它的根元素上使用 v-editable 指令,並傳遞它們接收的 blok 屬性
<div v-editable="blok"></div>
  • 最後,使用 <StoryblokComponent>,它在 Nuxt 應用程式中全域可用
<StoryblokComponent :blok="blok" />

blok 是來自 Storblok Content Delivery API 的實際 blok 資料。

2. 取得 Storyblok Stories 並監聽 Visual Editor 事件

Composition API

最簡單的方法是使用 useAsyncStoryblok 單行 composable(它是自動匯入的)。您需要將 slug 作為第一個參數傳遞,而第二個和第三個參數 apiOptionsbridgeOptions 分別是選用的。

請查看我們的 API 文件中可用的 apiOptions 以及傳遞至 Storyblok Bridge 的 bridgeOptions

注意 如果您想瞭解更多關於版本控制 { version: "draft" /* or "publish" */ } 的資訊,請前往 Working with preview and/or production environments 區段

<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 期間)保留。

渲染 Rich Text

您可以使用 StoryblokRichText 元件來渲染 rich text 欄位

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

或者,您可以使用 useStoryblokRichText composable 來獲得更多控制權

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

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

<template>
  <root />
</template>

如需更多您可以傳遞至 useStoryblokRichText 的絕佳選項,請參閱 完整選項 文件。

覆寫預設的 resolvers

您可以透過將 resolver prop 傳遞至 StoryblokRichText 元件來覆寫預設的 resolvers,例如,使用 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 composable,您可以透過 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>

舊版渲染 Rich Text

!警告
舊版 richTextResolver 即將被棄用。我們建議改用上述的新方法。

您可以輕鬆地使用 renderRichText 函式來渲染 rich text,該函式隨附於 @storyblok/nuxt 和 Vue computed property

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

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

您也可以透過將選項作為 renderRichText 函式的第二個參數傳遞來設定**自訂 Schema 和元件 resolver**

<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'公開存取權杖

注意 如果您將生產環境作為行銷人員和您的公開網站的預覽使用,您將需要一個 plugin 來處理不同的 .env 變數,或使用預覽存取權杖的版本,並檢查您是否在 Storyblok 內部。例如,類似於 if (window.location.search.includes(_storyblok_tk[token]=<YOUR_TOKEN>) 的程式碼。

請查看關於如何 存取不同內容版本 的官方文件。

使用 Nuxt 處理不同內容版本的建議方式是結合使用環境變數和 Nuxt runtime config,以在您的應用程式中公開組態和密碼

在您的 nuxt.config.ts

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

然後您可以在您的元件中存取 runtime config

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 Visual Editor 上發生任何變更時更新 story。

Storyblok JavaScript SDK 生態系統

A visual representation of the Storyblok JavaScript SDK Ecosystem

更多資源

支援

貢獻

請參閱我們的貢獻指南和我們的行為準則。此專案使用semantic-release,透過使用 commit message 來產生新版本,而我們使用 Angular Convention 來命名 commit。請查看 semantic-release FAQ 中關於此問題