news 2026/1/20 5:17:47

手把手教你用 Spring Boot + Vue 搭建个人博客系统(前端篇)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你用 Spring Boot + Vue 搭建个人博客系统(前端篇)

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!


一、为什么需要前端篇?

在上一篇《后端篇》中,我们已经用Spring Boot + MyBatis-Plus搭建好了博客系统的 RESTful API。现在,是时候让这些接口“活”起来——通过Vue 3 + Vite + Axios构建一个清爽、响应式的博客前台页面。

即使你是 Vue 小白,只要会 HTML 和一点 JavaScript,也能跟着一步步完成!


二、需求场景回顾

我们的博客前端要实现以下功能:

  1. 首页:展示所有文章列表(标题 + 分类 + 发布时间);
  2. 文章详情页:点击某篇文章,查看完整内容;
  3. 简洁美观:不需要复杂后台,但要有良好的阅读体验;
  4. 与后端无缝对接:调用上一篇写好的/api/posts接口。

⚠️ 注意:本篇聚焦前台展示(读者视角),不包含后台管理(如写文章、登录等),后续可扩展。


三、技术选型(前端)

技术作用
Vue 3 (Composition API)响应式框架,构建用户界面
Vite新一代前端构建工具,启动快、热更新快
Axios发送 HTTP 请求,调用后端 API
Vue Router实现页面路由跳转(首页 ↔ 详情页)
Tailwind CSS(可选)快速美化 UI(本文用原生 CSS 简化)

四、项目初始化

1. 创建 Vue 3 项目(使用 Vite)

npm create vue@latest blog-frontend

按提示选择:

  • ✔ Project name:blog-frontend
  • ✔ Add Vue Router for Single Page Application development?Yes
  • 其他选 No(简化)

进入目录并安装依赖:

cd blog-frontend npm install npm install axios npm run dev

默认访问http://localhost:5173


五、核心代码实现

1. 配置 Axios(统一 API 调用)

创建src/api/index.js

// src/api/index.js import axios from 'axios'; const api = axios.create({ baseURL: 'http://localhost:8080/api', // 后端地址(Spring Boot 默认端口) timeout: 5000, }); export default api;

💡 注意:如果你的 Spring Boot 运行在 8080,而 Vue 在 5173,会遇到跨域问题
临时解决方案:在 Spring Boot 的 Controller 上加@CrossOrigin(开发阶段可用)。

@RestController @RequestMapping("/api/posts") @CrossOrigin(origins = "http://localhost:5173") // ← 加这一行 public class PostController { // ... }

2. 定义数据类型(TypeScript 可选,这里用 JS 注释说明)

我们假设后端返回的文章结构如下:

{ "id": 1, "title": "Spring Boot 入门", "content": "详细内容...", "categoryName": "Java", "createTime": "2026-01-05T10:00:00" }

3. 首页:文章列表(HomeView.vue)

修改src/views/HomeView.vue

<template> <div class="container"> <h1>我的技术博客</h1> <div v-if="loading">加载中...</div> <div v-else-if="error" class="error">{{ error }}</div> <div v-else class="posts"> <div v-for="post in posts" :key="post.id" class="post-item" @click="goToDetail(post.id)" > <h2>{{ post.title }}</h2> <p class="meta"> <span class="category">{{ post.categoryName }}</span> <span class="time">{{ formatDate(post.createTime) }}</span> </p> </div> </div> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import { useRouter } from 'vue-router'; import api from '@/api'; const posts = ref([]); const loading = ref(true); const error = ref(null); const router = useRouter(); onMounted(async () => { try { const response = await api.get('/posts'); posts.value = response.data; } catch (err) { error.value = '加载文章失败,请检查后端是否运行'; console.error(err); } finally { loading.value = false; } }); function goToDetail(id) { router.push(`/post/${id}`); } function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString('zh-CN'); } </script> <style scoped> .container { max-width: 800px; margin: 0 auto; padding: 20px; } .post-item { border-bottom: 1px solid #eee; padding: 15px 0; cursor: pointer; } .post-item:hover { background-color: #f9f9f9; } .meta { color: #666; font-size: 0.9em; } .category { background: #e0f7fa; padding: 2px 6px; border-radius: 4px; margin-right: 10px; } </style>

4. 详情页:文章内容(PostView.vue)

创建src/views/PostView.vue

<template> <div class="container"> <button @click="goBack" class="back-btn">← 返回首页</button> <div v-if="loading">加载中...</div> <div v-else-if="error" class="error">{{ error }}</div> <article v-else> <h1>{{ post.title }}</h1> <div class="meta"> <span class="category">{{ post.categoryName }}</span> <span class="time">{{ formatDate(post.createTime) }}</span> </div> <div class="content" v-html="post.content"></div> </article> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import api from '@/api'; const route = useRoute(); const router = useRouter(); const post = ref(null); const loading = ref(true); const error = ref(null); onMounted(async () => { const id = route.params.id; try { const response = await api.get(`/posts/${id}`); post.value = response.data; } catch (err) { error.value = '文章不存在或加载失败'; } finally { loading.value = false; } }); function goBack() { router.go(-1); } function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString('zh-CN'); } </script> <style scoped> .container { max-width: 800px; margin: 0 auto; padding: 20px; } .back-btn { margin-bottom: 20px; background: #007bff; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } .content { line-height: 1.6; margin-top: 20px; } </style>

5. 配置路由(router/index.js)

确保src/router/index.js包含详情页路由:

import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' import PostView from '../views/PostView.vue' const routes = [ { path: '/', name: 'home', component: HomeView }, { path: '/post/:id', name: 'post', component: PostView, props: true } ] const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes }) export default router

六、反例 & 常见坑点

❌ 反例1:直接在模板里写v-html不过滤(XSS 风险)

<!-- 危险!如果 content 来自用户输入,可能注入恶意脚本 --> <div v-html="post.content"></div>

✅ 正确做法:

  • 如果内容是你自己写的(可信),可接受;
  • 如果支持用户评论/投稿,必须用 DOMPurify 等库过滤 HTML

❌ 反例2:硬编码 API 地址

// 不好维护! axios.get('http://localhost:8080/api/posts')

✅ 正确做法:统一配置baseURL(如我们做的api.js)。


❌ 反例3:忽略加载状态和错误处理

用户看到空白页面会以为“网站坏了”。

✅ 正确做法:显示 loading / error 提示,提升用户体验。


七、注意事项

  1. 跨域问题:开发时前后端端口不同(8080 vs 5173),务必处理 CORS;
  2. 后端必须运行:启动 Spring Boot 项目后再运行 Vue;
  3. 内容安全:若未来支持富文本编辑,务必对 HTML 进行消毒;
  4. 部署建议:生产环境可将 Vue 打包后放入 Spring Boot 的static目录,或用 Nginx 代理。

八、效果预览

  1. 启动后端:./mvnw spring-boot:run
  2. 启动前端:npm run dev
  3. 访问http://localhost:5173
  4. 点击文章 → 跳转详情页 ✅

九、下一步扩展方向

  • 添加Markdown 渲染(用marked库);
  • 增加分页无限滚动
  • 实现搜索分类筛选
  • 搭建后台管理页面(需登录 + JWT 鉴权)。

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/18 13:33:03

HuggingFace镜像网站同步上线GLM-4.6V-Flash-WEB支持,下载提速3倍

HuggingFace镜像网站同步上线GLM-4.6V-Flash-WEB支持&#xff0c;下载提速3倍 在当今多模态AI技术迅猛发展的背景下&#xff0c;图文理解、视觉问答和内容审核等跨模态任务正从实验室走向真实业务场景。然而&#xff0c;一个长期困扰国内开发者的现实问题是&#xff1a;如何快速…

作者头像 李华
网站建设 2026/1/19 15:32:38

深度学习计算机毕设之基于人工智能CNN卷积神经网络对辣椒类别识别

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/1/19 14:19:24

基于微信小程序的奶茶点单系统(毕设源码+文档)

课题说明随着新式茶饮行业的快速发展&#xff0c;线下奶茶店传统点单模式普遍存在排队时间长、点单效率低、个性化需求适配不足、订单管理繁琐等问题&#xff0c;难以满足消费者便捷化、多元化的消费需求&#xff0c;也制约了门店运营效率的提升。本课题聚焦这一痛点&#xff0…

作者头像 李华
网站建设 2026/1/17 14:46:00

Lovable Cloud 与 Supabase 强强联合:AI 应用构建工具的默认平台

Lovable近期推出Lovable CloudLovable近期推出了Lovable Cloud&#xff0c;使通过AI智能体进行应用开发变得前所未有的简便。所有在Lovable Cloud中创建的项目&#xff0c;背后均由Supabase提供强大支持。这意味着每一位使用Lovable的AI应用构建者&#xff0c;无论他们是否意识…

作者头像 李华
网站建设 2026/1/19 22:47:06

基于微信小程序的篮球场馆预订系统毕设源码+文档+讲解视频

前言 随着全民运动意识的提升&#xff0c;篮球运动参与度持续增高&#xff0c;但传统篮球场馆预订模式&#xff08;如电话预订、现场预订&#xff09;存在信息不透明、预订流程繁琐、场地档期查询不便、退改机制不灵活等问题&#xff0c;难以适配大众碎片化、便捷化的运动消费需…

作者头像 李华