构建基于 Storyblok 和 Nuxt 的作品集网站
在当今数字化时代,拥有一个展示个人或团队作品的作品集网站至关重要。本文将详细介绍如何使用 Storyblok 作为无头内容管理系统(CMS),结合 Nuxt 框架来构建一个功能强大且易于维护的作品集网站。
1. 前期准备
在开始项目开发之前,我们需要做一些准备工作。首先,我们将使用 Storyblok(https://www.storyblok.com/)来管理和存储内容,它提供了一个优秀的无头 CMS 解决方案,并且有免费版本可供使用。无头 CMS 主要关注内容本身,并将内容与展示分离,这种分离有助于构建可扩展的应用程序。完成作品集构建后,我们将使用 Netlify(https://www.netlify.com/)将其发布到公共 URL 上。
为了避免在开发过程中频繁切换上下文,建议提前准备好一些数据。对于每个要展示的项目,最好准备几张截图(可以使用 Screenshot.rocks 浏览器插件:https://screenshot.rocks/)和项目描述。
2. 设置 Storyblok
- 注册并创建新空间 :在 Storyblok 网站上注册账号,注册完成后会进入一个演示空间,我们可以跳过演示,选择创建一个新空间,并为其选择合适的名称和服务器位置。
- 选择计划 :创建空间后,可能会遇到一个关于试用期限的模态框,可以直接选择免费社区计划,也可以在 14 天试用期后再选择。
- 熟悉界面 :欢迎屏幕主要关注设置、内容、块库和资产部分。打开内容部分,会看到一个名为 Home 的条目,通过它可以在 Storyblok 环境中进行上下文内预览,即加载我们的作品集以提供非常逼真的预览效果。要实现这一点,需要获取访问令牌(Access token)来连接 CMS 和我们的应用程序。
3. 初始化 Nuxt 作品集项目
- 创建项目 :使用 nuxi Nuxt CLI 工具创建一个新项目:
npx nuxi@3.8.0 init portfolio
选择 npm 作为包管理器。
-
生成 SSL 证书
:如果尚未安装 mkcert 工具(https://github.com/FiloSottile/mkcert)来生成 SSL 证书,需要按照相应的安装说明进行安装。安装完成后,在命令行界面使用以下命令生成 localhost 证书:
mkcert localhost
-
更新开发脚本
:更新 package.json 文件中的开发脚本,以便在 SSL 模式下运行 Nuxt 开发服务器。找到
"dev": "nuxt dev",并将其替换为:
"dev": "NODE_TLS_REJECT_UNAUTHORIZED=0 nuxt dev --dotenv .env --https --ssl-cert localhost.pem --ssl-key localhost-key.pem",
- 创建 .env 文件 :创建一个 .env 文件,包含以下信息(访问令牌值可以在 Storyblok 的欢迎页面找到):
NUXT_STORYBLOK_ACCESS_TOKEN=replace_with_your_access_token
如果跳过了 Storyblok 欢迎页面,可以通过 Storyblok 菜单导航到设置,然后选择访问令牌来查看该令牌。
-
启动开发服务器
:在终端中运行
npm run dev
命令启动开发服务器,现在可以通过 HTTPS 协议访问服务器。在 Storyblok 欢迎页面的“设置预览 URL”字段中填写本地 URL(注意不要忘记末尾的斜杠)。如果在 Storyblok 中遇到错误,可能需要先在浏览器中打开该 URL,接受浏览器对本地证书的提示,然后重新加载 Storyblok 界面,应该会显示 Nuxt 欢迎页面。
4. 安装 Nuxt 模块
- 安装 Storyblok 模块 :停止 Nuxt 服务器,在终端中使用以下命令安装 Storyblok 模块:
npm i @storyblok/nuxt@5.7.4
Storyblok Nuxt 模块是一个 SDK,它与 Nuxt 有很好的集成,支持自动导入,并能轻松地将 Vue 组件直接映射到 Storyblok 实体。
-
配置 Storyblok 模块
:打开 nuxt.config.ts 文件并进行如下修改:
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
modules: [
['@storyblok/nuxt', {
accessToken: process.env.NUXT_STORYBLOK_ACCESS_TOKEN,
apiOptions: {
region: "eu"
}
}]
],
})
如果在不同地区操作,请选择相应的区域。
-
安装 UI 库
:同时,我们可以安装 UI 库(https://ui.nuxt.com/),在终端中使用以下命令:
npm i @nuxt/ui@2.9.0
安装完成后,在 nuxt.config.ts 文件的 modules 属性中添加该模块的名称:
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
modules: [
['@storyblok/nuxt', {
accessToken: process.env.NUXT_STORYBLOK_ACCESS_TOKEN,
apiOptions: {
region: "eu"
}
}],
'@nuxt/ui'
],
})
5. 测试内容显示
在向 CMS 添加自己的内容之前,我们先测试是否能在应用程序中显示 Storyblok 的内容。
-
设置主页路径
:打开内容部分的 Home 条目,使用条目配置,通过“真实路径”属性告诉 Storyblok 该页面发布在域名的根目录。
-
创建组件
:为了使 Storyblok 的块库内容映射到 Vue 组件,需要在项目根目录的 ./storyblok 文件夹中创建相应的组件。首先创建 Page.vue 文件:
<template>
<div v-editable="blok">
<StoryblokComponent v-for="blok in blok.body" :key="blok._uid" :blok="blok" />
</div>
</template>
<script setup lang="ts">
defineProps({ blok: Object })
</script>
然后创建 Teaser.vue 文件:
<template>
<div v-editable="blok" v-if="blok">
{{ blok.headline }}
</div>
</template>
<script setup lang="ts">
defineProps({ blok: Object });
</script>
注意,v-editable 指令很重要,它向 Storyblok 表明这些块的内容是可编辑的。
-
创建页面
:在应用程序中创建 ./pages 文件夹,并临时创建一个 index.vue 文件:
<script setup>
const story = await useAsyncStoryblok('home')
</script>
<template>
<StoryblokComponent v-if="story" :blok="story.content" />
</template>
- 创建布局 :为网站创建一个简单的布局。在 ./components 文件夹中放置 Header.vue 文件(https://github.com/PacktPublishing/Building-Real-world-Web-Applications-with-Vue.js-3/blob/main/10.portfolio/.notes/10.1-Header.vue)和 Footer.vue 文件(https://github.com/PacktPublishing/Building-Real-world-Web-Applications-with-Vue.js-3/blob/main/10.portfolio/.notes/10.2-Footer.vue)。在 ./layouts 文件夹中创建 default.vue 文件:
<template>
<div class="flex flex-col min-h-screen">
<Header />
<div class="flex-grow">
<main class="container mx-auto">
<slot />
</main>
</div>
<Footer />
</div>
</template>
完成上述操作后,移除项目根目录的 app.vue 文件。在 Storyblok 界面中,会看到“Hello world!”消息,替换了 Nuxt 欢迎视图。在浏览器中打开开发服务器也会看到相同的结果。现在,如果在 Storyblok 中更改文本并保存,更改会同时反映在 Storyblok 界面和浏览器中。
6. 处理多种内容类型
在 Storyblok 的块库中,默认有四个现有块:feature、grid、page 和 teaser,其中 page 是内容类型,其余是可嵌套类型。内容类型是顶级内容类型,可以看作是具有唯一 URL 的页面,用于容纳较低级别的可嵌套内容类型,这些可嵌套类型是页面的构建块。还有通用类型,但在本示例中不使用。
为了满足作品集的需求,我们创建一个新块:
-
创建新块
:点击“+ 新块”按钮,技术名称填写为 portfolio(建议使用小写),这将是一个内容类型的块。点击“添加块”按钮配置类型,然后添加以下必要字段:
1. 添加一个文本类型的标题字段。
2. 添加一个资产类型的图像字段,并配置该字段只允许图片。
3. 添加一个文本区域类型的描述字段,并配置该字段允许的最大长度为 150 个字符。
4. 添加一个富文本类型的正文字段。
7. 配置作品集
- 创建文件夹 :我们希望作品集发布在 /portfolio 路径下,在内容部分选择“+ 创建新”来创建一个名为 Portfolio 的文件夹参数,slug 会自动填充。注意内容类型字段,目前可以保持默认设置。点击“创建”将文件夹添加到内容中。
- 创建主页条目 :为了在作品集的根目录展示项目概述,导航到该文件夹并创建一个新条目,选择“故事”,名称选择 Home,并在创建时勾选“定义为文件夹的根”复选框。内容类型使用默认的 Page。由于我们尚未定义该路由的目标,Storyblok 尝试打开页面时可能会出现服务器不可用或 404 未找到错误,这在后续“内容映射到代码”部分会解决。再次使用条目配置并勾选“定义为文件夹的根”复选框,此选项会移除 slug 值。
- 限制内容类型 :虽然这里添加了 page 类型,但我们希望限制其余内容只包含 Portfolio 条目。回到 Storyblok 内容部分的根目录,勾选 Portfolio 文件夹条目,再次编辑文件夹属性,勾选“限制内容类型”选项并选择 Portfolio 类型。这不会影响之前创建的主页,但会影响未来的条目。现在可以创建几个作品集条目,建议至少添加两个项目。
8. 内容映射到代码
- 替换页面文件 :由于现在有多个常规页面需要处理,我们可以完全移除 ./pages/index.vue 文件,并用 ./pages/[…slug].vue 文件替换:
<script setup lang="ts">
const { slug } = useRoute().params as { slug: string[] };
const story = await useAsyncStoryblok(
slug && slug.length > 0 ? slug.join("/") : "home", { version: "draft" }
);
</script>
<template>
<div>
<StoryblokComponent v-if="story" :blok="story.content" />
</div>
</template>
有了这段代码,应该能够渲染一个简单的页面。在 Storyblok 中打开作品集的主页,会看到一个空白页面。在右侧的内容面板中,可以添加一个新块,例如 Teaser 块并为其添加合适的标题。在浏览器中访问开发 URL 时,预览效果会立即显示。
-
同步组件
:注意到 ./storyblok 文件夹的内容与块库中的块数量可能不匹配,需要确保它们始终保持一致。在 ./storyblok 文件夹中创建 Feature.vue 组件:
<script setup lang="ts">
defineProps({ blok: Object });
</script>
<template>
<div v-editable="blok" v-if="blok">
<h3>
{{ blok.name }}
</h3>
</div>
</template>
再创建 Grid.vue 文件:
<script setup lang="ts">
defineProps({
blok: {
type: Object as () => { columns: any },
required: true,
},
});
</script>
<template>
<div v-editable="blok" v-if="blok" class="flex mx-auto">
<StoryblokComponent
v-for="blok in blok.columns"
:key="blok._uid"
:blok="blok"
/>
</div>
</template>
Grid.vue 文件与 Feature.vue 文件略有不同,因为它可以包含嵌套的 Storyblok 组件,通过 可以渲染每个可能递归级别的正确组件。
9. 展示作品集部分
作品集部分大致分为两种情况:在该部分的主页展示所有作品集项目的概述,以及在单独的页面展示单个项目。
-
创建概述组件
:在块库中创建一个新块,命名为 portfolio-all,使其成为可嵌套块。在字段编辑器中添加以下字段:
1. 添加一个文本类型的标题字段。
2. 添加一个文本区域类型的描述字段,并配置该字段允许的最大长度为 150 个字符。
3. 添加一个富文本类型的正文字段。
保存后,更新作品集主页,移除标题块,拖入新的 Portfolio All 组件并添加一些合理的文本。根据命名约定,在 ./storyblok 文件夹中创建 PortfolioAll.vue 组件(https://github.com/PacktPublishing/Building-Real-world-Web-Applications-with-Vue.js-3/blob/main/10.portfolio/.notes/10.3-PortfolioAll.vue):
<script setup lang="ts">
import type { Ref } from "vue";
import type { StoryblokProject, StoryBlok } from "@/types/storyblok";
const props = defineProps({
blok: { type: Object as () => StoryBlok, required: true, },
});
const projects: Ref<StoryblokProject[] | null> = ref(null);
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get("cdn/stories", {
version: "draft",
starts_with: "portfolio",
is_startpage: false,
});
const richTextBody = computed(() => renderRichText(props.blok.body));
projects.value = data.stories;
</script>
<template>
<div>
<h1 class="text-2xl mb-4 text-primary">
{{ blok.headline }}
</h1>
<div v-html="richTextBody" class="py-4" />
<div class="grid grid-cols-2 gap-4">
<UCard
v-for="project in projects"
<!-- 后续代码可能缺失,但不影响整体理解 -->
</div>
</div>
</template>
组件相对复杂一些,它与 Page 组件类似,会接收 props,同时还使用 useStoryblokApi 组合式函数手动请求额外的数据,获取 portfolio 路径下的所有故事,并排除作品集部分的主页。接收到数据后,将其存储在 projects 引用中,以便在模板中迭代,并使用 Nuxt UI 的
组件渲染到项目页面的链接。由于我们为主页正文定义了富文本字段,使用 Storyblok 的 renderRichText 函数将其作为计算值来确保正确渲染。
-
创建详情页面组件
:为了构建作品集详情页面,在 ./storyblok 文件夹中创建对应的 Portfolio.vue 组件(https://github.com/PacktPublishing/Building-Real-world-Web-Applications-with-Vue.js-3/blob/main/10.portfolio/.notes/10.4-Portfolio.vue)。由于我们已经预定义了 Portfolio 块的字段,该组件可以简单地使用这些内容来渲染一个常见的 Vue 模板,数据来自 ./pages/[…slug.vue] 组件,具体是 useAsyncStoryblok 组合式函数传递给
的。
通过以上步骤,我们可以成功构建一个基于 Storyblok 和 Nuxt 的作品集网站,实现内容的管理、展示和编辑功能。在整个过程中,我们利用了 Storyblok 的强大功能和 Nuxt 的便捷性,使得网站的开发和维护变得更加高效。
总结
本文详细介绍了使用 Storyblok 和 Nuxt 构建作品集网站的全过程,包括前期准备、环境搭建、内容管理、组件创建和页面配置等方面。通过遵循这些步骤,你可以轻松打造一个个性化且功能丰富的作品集网站,展示你的优秀作品。同时,这种架构也具有良好的可扩展性,方便后续添加更多内容和功能。希望本文能对你有所帮助,祝你在网站开发的道路上取得成功!
构建基于 Storyblok 和 Nuxt 的作品集网站
10. 深入理解内容映射机制
为了更好地理解内容是如何从 Storyblok 映射到我们的 Nuxt 应用程序中的,我们可以通过一个流程图来展示这个过程。
graph LR
A[Storyblok 内容] --> B[useAsyncStoryblok 查询]
B --> C[获取内容数据]
C --> D[<StoryblokComponent> 组件]
D --> E[匹配 ./storyblok 文件夹组件]
E --> F[渲染页面内容]
当我们在 Storyblok 中创建和编辑内容时,通过
useAsyncStoryblok
这个组合式函数,我们可以从 Storyblok 的 CDN 获取到相应的内容数据。然后,
<StoryblokComponent>
组件会根据内容的类型,在
./storyblok
文件夹中查找匹配的 Vue 组件,最后将内容渲染到页面上。
例如,当我们访问
./pages/[...slug].vue
页面时,
useAsyncStoryblok
会根据 URL 中的
slug
参数来查询 Storyblok 中的内容。如果
slug
对应一个
portfolio
类型的内容,那么
<StoryblokComponent>
就会找到
./storyblok/Portfolio.vue
组件,并将内容传递给它进行渲染。
11. 优化网站性能
在构建网站的过程中,性能优化是一个重要的环节。以下是一些可以优化我们基于 Storyblok 和 Nuxt 的作品集网站性能的方法:
-
图片优化
:在 Storyblok 中添加图片时,确保图片的尺寸和质量是合适的。可以使用图片压缩工具对图片进行压缩,减少图片的文件大小。同时,对于不同的设备和屏幕尺寸,可以使用响应式图片技术,只加载合适尺寸的图片。
-
缓存机制
:利用 Nuxt 的缓存功能,对一些不经常变化的内容进行缓存。例如,对于作品集项目的列表页面,可以设置缓存时间,减少对 Storyblok API 的请求次数。在
nuxt.config.ts
中可以进行如下配置:
export default defineNuxtConfig({
// ...其他配置
nitro: {
preset: 'node-server',
routeRules: {
'/portfolio': { cache: { maxAge: 3600 } }, // 缓存 1 小时
},
},
})
- 代码分割 :Nuxt 会自动进行代码分割,但我们也可以手动优化。对于一些不常用的组件,可以使用动态导入的方式,减少首屏加载的代码量。例如:
<script setup>
const loadPortfolioComponent = () => import('@/storyblok/Portfolio.vue')
</script>
12. 实现实时预览和编辑功能
Storyblok 提供了强大的实时预览和编辑功能,让我们可以在编辑内容的同时,实时看到页面的变化。
-
实时预览
:在前面的步骤中,我们已经配置了 Storyblok 的预览 URL,当我们在 Storyblok 中编辑内容时,通过这个预览 URL 可以实时看到页面的变化。确保在 Storyblok 的欢迎页面中正确填写了预览 URL,并且在浏览器中接受了本地证书的提示。
-
实时编辑
:通过
v-editable
指令,我们可以让 Storyblok 知道哪些内容是可编辑的。当我们在 Storyblok 中点击这些可编辑的区域时,就可以直接在 Storyblok 的界面中进行编辑,编辑完成后保存,页面会立即更新。例如,在
Page.vue
和
Teaser.vue
组件中,我们都使用了
v-editable
指令:
<template>
<div v-editable="blok">
<StoryblokComponent v-for="blok in blok.body" :key="blok._uid" :blok="blok" />
</div>
</template>
13. 处理错误和异常情况
在开发过程中,我们需要考虑到可能出现的错误和异常情况,并进行相应的处理。
-
网络错误
:当我们使用
useAsyncStoryblok
从 Storyblok 的 CDN 获取内容时,可能会遇到网络错误。可以在
useAsyncStoryblok
调用时添加错误处理逻辑,例如:
<script setup>
try {
const story = await useAsyncStoryblok('home')
} catch (error) {
console.error('获取内容时出现网络错误:', error)
// 可以在这里显示一个错误提示信息给用户
}
</script>
-
组件匹配错误
:如果在
./storyblok文件夹中没有找到与 Storyblok 内容匹配的组件,可能会导致页面无法正确渲染。我们可以在<StoryblokComponent>组件中添加一个默认的处理逻辑,当找不到匹配组件时,显示一个提示信息:
<template>
<div v-if="story">
<StoryblokComponent v-if="story" :blok="story.content">
<template #fallback>
<p>未找到匹配的组件来渲染此内容。</p>
</template>
</StoryblokComponent>
</div>
</template>
14. 部署网站
完成网站的开发和测试后,就可以将网站部署到生产环境中了。我们可以使用 Netlify(https://www.netlify.com/)来进行部署。
-
连接 Netlify 和 GitHub
:将我们的项目代码托管在 GitHub 上,然后在 Netlify 中连接到我们的 GitHub 仓库。
-
配置部署设置
:在 Netlify 中设置部署命令和环境变量。部署命令可以是
npm run build
,环境变量需要设置
NUXT_STORYBLOK_ACCESS_TOKEN
等。
-
触发部署
:当我们将代码推送到 GitHub 仓库时,Netlify 会自动触发部署流程,将我们的网站部署到生产环境中。
15. 持续维护和更新
网站部署完成后,并不意味着工作就结束了。我们需要持续对网站进行维护和更新。
-
内容更新
:定期在 Storyblok 中更新作品集的内容,添加新的项目,更新项目的描述和图片等。
-
技术更新
:关注 Nuxt 和 Storyblok 的官方文档和更新日志,及时更新我们使用的模块和工具版本,以获取更好的性能和功能。
-
用户反馈
:收集用户的反馈意见,根据用户的需求和建议,对网站进行优化和改进。
总结
通过本文的详细介绍,我们全面了解了如何使用 Storyblok 和 Nuxt 构建一个功能强大、易于维护的作品集网站。从前期的准备工作,到环境的搭建、内容的管理、组件的创建和页面的配置,再到性能优化、错误处理、部署和持续维护,每一个环节都至关重要。
这种基于无头 CMS 和现代前端框架的架构,让我们可以将内容管理和页面展示分离,提高了开发的效率和灵活性。同时,Storyblok 提供的实时预览和编辑功能,以及 Nuxt 的便捷性和可扩展性,使得网站的开发和维护变得更加轻松。
希望本文能为你在构建作品集网站的过程中提供有价值的参考,让你能够打造出一个展示自己优秀作品的理想平台。在未来的网站开发中,不断探索和尝试新的技术和方法,让你的网站更加出色。
构建作品集网站:Storyblok与Nuxt的结合
超级会员免费看
600

被折叠的 条评论
为什么被折叠?



