首发于Enaium的个人博客
引言
大家可能刚学会Java
和Vue
之后都会想下一步是什么?那么就先把SpringBoot
和Vue
结合起来,做一个前后端分离的项目吧。
准备工作
首先你需要懂得Java
和Vue
的基础知识,环境这里就不多说了,直接开始。
创建 SpringBoot 项目
使用IDEA
旗舰版的可以直接使用自带Spring Initializr
创建项目,其他的可以使用Spring Initializr创建项目。
语言选择Java
,类型选择Gradle-Kotlin
,Java
选择 21,其他的都随便填。
接下来选择依赖,这里选择web
,lombok
,数据库选择PostgreSQL
,如果你使用的是MySQL
就选它
之后点击创建自动打开项目,或者点击生成打开下载的项目
之后等待项目的依赖下载完成就好了
如果需要配置镜像那就在repositories
中最上面添加腾讯云的镜像
repositories {
maven {
url = uri("https://mirrors.cloud.tencent.com/nexus/repository/maven-public")
}
mavenCentral()
}
首先我们需要创建数据库,比如一个图书管理系统,需要有一张图书表,有一些字段,比如标题、作者、创建时间、等等。
我们使用数据库管理工具来创建一个表吧。
注意这里使用的是Postgres
,如果是MySQL
类型略有不同。
之后我们就可以创建实体类了,这里需要先引入ORM
框架依赖,这里我为了方便引入写了一个Gradle
插件,把它写入到plugins
中,接着在刷新一下项目就可以继续编写代码了。
plugins {
// 省略其他插件...
id("cn.enaium.jimmer.gradle") version "0.0.11"
}
我们创建一个接口Book
添加一个Entity
和Table
注解,之后添加一些方法,名称就是根据数据库中字段名称一样,只不过要把蛇形命名改为小驼峰。
@Entity
@Table(name = "book")
public interface Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
int id();
String title();
String author();
LocalDateTime createTime();
}
写完之后,我们按下编译的快捷键(默认是 Ctrl+F9),之后就可以编写接口了。
@RestController
@RequiredArgsConstructor
public class BookController {
private final JSqlClient sql;
@GetMapping("/book")
public List<Book> getBooks() {
return sql.createQuery(Tables.BOOK_TABLE).select(Tables.BOOK_TABLE).execute();
}
@PostMapping("/book")
public void saveBook(@RequestBody Book book) {
sql.save(book);
}
}
使用RequiredArgsConstructor
注解可以为被final
修饰的字段生成构造方法,这样就不用手动写构造方法了。
getBooks
用于获取图书列表,首先使用createQuery
创建一个查询,传入一张表,类似于from book
,接着使用select
选择所有字段,类似于select id, name, author, createTime
,最后使用execute
执行查询。
saveBook
用于保存图书,使用save
方法保存图书。
接下来需要配置允许跨域,这里使用CORS
,在SpringBoot
中配置CORS
很简单,只需实现WebMvcConfigurer
接口的addCorsMappings
方法即可。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("*")
.allowedHeaders("*");
}
}
最后我们配置一下数据库链接。
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?currentSchema=sbv
spring.datasource.username=postgres
spring.datasource.password=postgres
jimmer.dialect=org.babyfish.jimmer.sql.dialect.PostgresDialect
如果是MySQL
就把PostgresDialect
改为MySqlDialect
这样我们的后端就写完了,接下来我们开始写前端。
创建 Vue 项目
这里使用pnpm create vue
来创建,如果没有安装pnpm
可以使用npm install -g pnpm
来安装。
名称随便,之后使用Typescript
和Vue Router
剩下的选否。
之后使用命令pnpm install
安装依赖,并删除src
下的所有文件。
编写App.vue
<script setup lang="ts"></script>
<template>
<h1>Vue 3 + Vite + TypeScript</h1>
</template>
编写main.ts
import { createApp } from "vue"; import App from "./App.vue"; const app = createApp(App); app.mount("#app");
这里我使用naive ui
,使用命令安装pnpm add -D naive-ui
,之后使用自动导入配置。
安装这两个插件pnpm add -D unplugin-auto-import unplugin-vue-components
之后修改vite.config.ts
文件
import { fileURLToPath, URL } from "node:url"
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import AutoImport from "unplugin-auto-import/vite"
import Components from "unplugin-vue-components/vite"
import { NaiveUiResolver } from "unplugin-vue-components/resolvers"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: [
"vue",
{
"naive-ui": ["useDialog", "useMessage", "useNotification", "useLoadingBar"]
}
]
}),
Components({
resolvers: [NaiveUiResolver()]
})
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url))
}
}
})
然后就可以继续编写页面了,首先在views
中编写两个页面一个用来获取所有的图书,一个用来添加图书。
<script setup lang="ts">
import type { DataTableColumns } from "naive-ui"
import { ref } from "vue"
interface Book {
id: number
title: string
author: string
createTime: string
}
const columns: DataTableColumns<Book> = [
{
title: "ID",
key: "id"
},
{
title: "Title",
key: "title"
},
{
title: "Author",
key: "author"
},
{
title: "Create Time",
key: "createTime"
}
]
const books = ref<Book[]>([])
fetch("http://localhost:8080/book")
.then((response) => response.json())
.then((data: Book[]) => {
books.value = data
})
</script>
<template>
<n-data-table :columns="columns" :data="books" />
</template>
<script setup lang="ts">
import { ref } from "vue"
import { useMessage, type FormInst } from "naive-ui"
interface BookInput {
title?: string
author?: string
}
const message = useMessage()
const formRef = ref<FormInst | null>(null)
const bookInput = ref<BookInput>({})
const save = () => {
formRef.value?.validate().then((valid) => {
if (valid) {
fetch("http://localhost:8080/book", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(bookInput.value)
}).then(() => {
message.success("Book saved")
})
}
})
}
</script>
<template>
<n-form ref="formRef" :model="bookInput">
<n-form-item label="Title" path="title" :rule="[{ required: true, message: 'Please input title' }]">
<n-input v-model:value="bookInput.title" />
</n-form-item>
<n-form-item label="Author" path="author" :rule="[{ required: true, message: 'Please input author' }]">
<n-input v-model:value="bookInput.author" />
</n-form-item>
<n-button type="primary" @click="save">Save</n-button>
</n-form>
</template>
之后编写布局,在layouts
下编写一个,BookLayout.vue
,我们使用左侧一栏来选择页面,右侧来展示页面。
<script setup lang="ts"></script>
<template>
<n-layout has-sider>
<n-layout-sider content-style="padding: 24px;">
<ul>
<li>
<RouterLink to="/book">Book</RouterLink>
</li>
<li>
<RouterLink to="/book/create">Create Book</RouterLink>
</li>
</ul>
</n-layout-sider>
<n-layout>
<n-layout-content content-style="padding: 24px;">
<RouterView />
</n-layout-content>
</n-layout>
</n-layout>
</template>
之后创建一个router
目录,编写index.ts
文件
import BookLayout from "@/layouts/BookLayout.vue"
import Books from "@/views/Books.vue"
import SaveBook from "@/views/SaveBook.vue"
import { createRouter, createWebHistory } from "vue-router"
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/book",
component: BookLayout,
children: [
{
path: "",
component: Books
},
{
path: "create",
component: SaveBook
}
]
}
]
})
export default router
之后在main.ts
中引入router
import router from "./router"
// 省略其他代码...
app.use(router)
最后在App.vue
中使用RouterView
<template>
<NMessageProvider>
<RouterView />
</NMessageProvider>
</template>
这样我们的前端就写完了,接下来我们启动项目。
源码