大家好,我是一名前端开发者,专注于Vue生态和前端技术的分享!继上一篇文章带大家打造Vue 3组件库后,今天我们将深入探索Nuxt
3,开发一个企业级SSR(服务端渲染)应用。本文以一个博客系统为例,涵盖Nuxt
3项目搭建、Pinia状态管理、数据预取、性能优化和部署全流程,助你在企业项目中快速落地SSR!喜欢这篇内容?欢迎关注我的博客和公众号(微信搜索:小贺前端笔记),更多干货和实战项目等你来发现!
为什么选择Nuxt 3?
Nuxt 3是基于Vue 3的强大框架,特别适合企业级SSR应用:
- SEO友好:服务端渲染确保搜索引擎能抓取动态内容。
- 首屏性能:预渲染和数据预取提升用户体验。
- 开箱即用:内置路由、状态管理和构建工具,减少配置。
- 生态丰富:支持Pinia、VueUse、TypeScript等现代技术。
本文将以一个博客系统为例,展示如何用Nuxt 3实现企业级SSR应用。
1. 项目初始化
我们使用Nuxt 3搭建一个博客系统,支持文章列表、详情页和搜索功能。
1.1 创建项目
运行以下命令初始化Nuxt 3项目:
npx nuxi@latest init nuxt3-blog
cd nuxt3-blog
npm install
1.2 安装依赖
为支持状态管理和数据请求,安装以下依赖:
npm install @pinia/nuxt pinia @vueuse/nuxt axios
@pinia/nuxt
:集成Pinia状态管理。pinia
:轻量级状态管理库。@vueuse/nuxt
:提供VueUse工具函数。axios
:用于API请求。
1.3 项目结构
创建一个清晰的目录结构:
nuxt3-blog/
├── assets/ # 静态资源
│ └── css/
│ └── main.css
├── components/ # 组件
│ ├── ArticleCard.vue
│ └── SearchBar.vue
├── composables/ # 组合式函数
│ └── useApi.ts
├── layouts/ # 布局
│ └── default.vue
├── pages/ # 页面
│ ├── index.vue
│ └── article/[id].vue
├── stores/ # 状态管理
│ └── article.ts
├── public/ # 公共资源
├── nuxt.config.ts # Nuxt配置文件
├── package.json
└── README.md
2. 配置Nuxt 3
2.1 修改nuxt.config.ts
配置模块、CSS和TypeScript支持:
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@pinia/nuxt', '@vueuse/nuxt'],
css: ['~/assets/css/main.css'],
typescript: {
strict: true,
},
});
2.2 创建全局样式
在assets/css/main.css
中添加基础样式:
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
3. 开发核心功能
我们将实现博客的文章列表、详情页和搜索功能。
3.1 数据接口
假设使用JSONPlaceholder模拟后端API:
- 文章列表:
https://jsonplaceholder.typicode.com/posts
- 文章详情:
https://jsonplaceholder.typicode.com/posts/:id
在composables/useApi.ts
中封装API请求:
import axios from 'axios';
export const useApi = () => {
const baseURL = 'https://jsonplaceholder.typicode.com';
const getPosts = async () => {
const { data } = await axios.get(`${baseURL}/posts`);
return data;
};
const getPostById = async (id: string) => {
const { data } = await axios.get(`${baseURL}/posts/${id}`);
return data;
};
return { getPosts, getPostById };
};
3.2 状态管理
在stores/article.ts
中使用Pinia管理文章数据:
import { defineStore } from 'pinia';
export const useArticleStore = defineStore('article', {
state: () => ({
posts: [] as any[],
searchQuery: '',
}),
actions: {
setPosts(posts: any[]) {
this.posts = posts;
},
setSearchQuery(query: string) {
this.searchQuery = query;
},
},
getters: {
filteredPosts: (state) => {
if (!state.searchQuery) return state.posts;
return state.posts.filter((post) =>
post.title.toLowerCase().includes(state.searchQuery.toLowerCase())
);
},
},
});
3.3 组件开发
3.3.1 ArticleCard.vue
在components/ArticleCard.vue
中创建文章卡片组件:
<template>
<div class="article-card">
<h3>{{ article.title }}</h3>
<p>{{ article.body.slice(0, 100) }}...</p>
<NuxtLink :to="`/article/${article.id}`">阅读全文</NuxtLink>
</div>
</template>
<script setup lang="ts">
defineProps<{
article: { id: number; title: string; body: string };
}>();
</script>
<style scoped>
.article-card {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.article-card h3 {
margin: 0 0 10px;
}
.article-card a {
color: #409eff;
text-decoration: none;
}
</style>
3.3.4 SearchBar.vue
在components/SearchBar.vue
中创建搜索栏:
<template>
<input
v-model="searchQuery"
type="text"
placeholder="搜索文章..."
class="search-bar"
/>
</template>
<script setup lang="ts">
import { useArticleStore } from '~/stores/article';
const store = useArticleStore();
const searchQuery = computed({
get: () => store.searchQuery,
set: (value) => store.setSearchQuery(value),
});
</script>
<style scoped>
.search-bar {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #dcdcdc;
border-radius: 4px;
font-size: 16px;
}
</style>
3.4 布局
在layouts/default.vue
中定义默认布局:
<template>
<div>
<header>
<h1>Nuxt 3 博客</h1>
<nav>
<NuxtLink to="/">首页</NuxtLink>
</nav>
</header>
<main class="container">
<slot />
</main>
</div>
</template>
<style scoped>
header {
background: #409eff;
color: white;
padding: 20px;
text-align: center;
}
nav a {
color: white;
margin: 0 10px;
text-decoration: none;
}
h1 {
margin: 0;
}
</style>
3.5 页面开发
3.5.1 文章列表页(index.vue)
在pages/index.vue
中实现文章列表和搜索:
<template>
<div>
<SearchBar />
<ArticleCard
v-for="post in filteredPosts"
:key="post.id"
:article="post"
/>
</div>
</template>
<script setup lang="ts">
import { useArticleStore } from '~/stores/article';
import { useApi } from '~/composables/useApi';
const store = useArticleStore();
const { getPosts } = useApi();
const { data: posts } = await useAsyncData('posts', async () => {
const data = await getPosts();
store.setPosts(data);
return data;
});
const filteredPosts = computed(() => store.filteredPosts);
</script>
- 使用
useAsyncData
在服务端预取文章数据。 - 通过 Pinia 的
filteredPosts
getter 实现搜索过滤。
3.5.2 文章详情页(article/[id].vue)
在pages/article/[id].vue
中实现文章详情:
<template>
<div v-if="post">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
<NuxtLink to="/">返回首页</NuxtLink>
</div>
<div v-else>文章不存在</div>
</template>
<script setup lang="ts">
import { useApi } from '~/composables/useApi';
const route = useRoute();
const { getPostById } = useApi();
const { data: post } = await useAsyncData(`post-${route.params.id}`, () =>
getPostById(route.params.id)
);
</script>
<style scoped>
h2 {
margin-bottom: 20px;
}
p {
line-height: 1.6;
}
a {
color: #409eff;
text-decoration: none;
}
</style>
4. 性能优化
4.1 懒加载组件
为大型组件启用懒加载,减少首屏加载时间:
<script setup lang="ts">
const ArticleCard = defineAsyncComponent(() =>
import('~/components/ArticleCard.vue')
);
</script>
4.2 图片优化
在nuxt.config.ts
中启用图片优化(需安装@nuxt/image
):
npm install -D @nuxt/image
export default defineNuxtConfig({
modules: ['@pinia/nuxt', '@vueuse/nuxt', '@nuxt/image'],
image: {
provider: 'static',
},
});
4.3 缓存API响应
在composables/useApi.ts
中添加缓存:
import axios from 'axios';
import { useNuxtApp } from '#app';
export const useApi = () => {
const baseURL = 'https://jsonplaceholder.typicode.com';
const cache = new Map();
const getPosts = async () => {
if (cache.has('posts')) return cache.get('posts');
const { data } = await axios.get(`${baseURL}/posts`);
cache.set('posts', data);
return data;
};
const getPostById = async (id: string) => {
const cacheKey = `post-${id}`;
if (cache.has(cacheKey)) return cache.get(cacheKey);
const { data } = await axios.get(`${baseURL}/posts/${id}`);
cache.set(cacheKey, data);
return data;
};
return { getPosts, getPostById };
};
5. 部署到Vercel
5.1 配置Vercel
-
安装Vercel CLI:
npm install -g vercel
-
部署项目:
vercel
-
配置环境变量(如果有API密钥):
在Vercel仪表板中添加环境变量。
5.2 注意事项
- 确保
nuxt.config.ts
中设置ssr: true
。 - 检查
package.json
的scripts
中有"build": "nuxt build"
。 - 使用 Vercel 的自动缩放功能优化性能。
6. 常见问题与解决
- Q:首屏加载慢?
A:使用defineAsyncComponent
懒加载组件,或启用@nuxt/image
优化图片。 - Q:数据预取失败?
A:检查useAsyncData
的 key 是否唯一,确保 API 响应正常。 - Q:部署后样式丢失?
A:确认assets/css/main.css
已正确引入,检查 Vercel 的构建日志。
7. 总结
通过以上步骤,我们用Nuxt 3开发了一个企业级SSR博客系统。核心要点包括:
- 使用
useAsyncData
实现服务端数据预取。 - 集成 Pinia 和 VueUse 提升开发效率。
- 通过懒加载和缓存优化性能。
- 部署到 Vercel 实现高可用性。
相关推荐
想深入学习Vue 3和前端开发?以下是几篇值得一读的文章:
你在使用Nuxt 3开发SSR应用时遇到过哪些挑战?欢迎在评论区分享你的经验!💬