前言
根据网上的一些教程试着搭建了一个简单的前后端分离的用户管理系统。该系统使用Vue框架编写前端代码,Springboot编写后端代码,Mysql作为数据库存储系统的数据。本文着重介绍整个项目的搭建流程以及附加一些在搭建过程的想法和注意事项。
一、Vue及前端项目框架搭建
1.1、什么是Vue
官方解释:Vue是一套用于构建用户界面的渐进式框架。
Vue是一个js框架,提供了一套开发规则,按照这个开发规则就可以提高开发效率。
补充:渐进式意思是,vue.js本身功能局限,一旦配合其他的工具可以增强其功能。
1.2、使用Vue需要做哪些准备
1、官网下载Node.js;
2、在安装的目录下创建名为node_cache和node_global的全局安装目录和缓存日志目录的两个文件夹;
3、根据需要配置和修改环境变量(主要针对npm命令)
4、安装vue npm install vue -g
5、安装webpack模板 npm install webpack -g
6、安装脚手架vue-cli npm install vue-cli -g
7、安装vue-router npm install vue-router -g
1.3、创建Vue项目模板
1、在终端输入vue create vue_project_name;
2、然后根据后面的提示初始化vue项目;
3、进入vue_project_name文件夹,然后运行npm 或者 yarn 命令启动vue项目;
1.4 Vue项目目录中各文件夹及文件的作用
1.4.1Vue项目文件结构
1.4.2各文件夹及文件的作用
- node_modules:项目所需要的依赖包,如果node_modules被删除,只需要npm install重新加载就好;
- public:任何放置在publlic文件夹的静态资源都会被简单的复制,而不经过webpack。需要通过绝对路径来引用它们。
- components:放置通用模块组件;
- layout:将App.vue中的布局单独存放在一个通用布局中(如果在该项目中除了用户管理组件User.vue,还可能存在书籍管理组件Book.vue,课程管理Course.vue等,这些视图中都有Header.vue和Aside.vue两个公共的组件,为了统一布局,使App.vue中呈现的布局不太混乱,可以使项目布局更有条理);
- router:编写一些路由的文件配置
- store:放置vuex需要的状态关联文件,设置公共的state,mutations;
- view:存放视图.vue文件;
- util:存放一些配置类工具文件如request.js文件;
- App.vue:形成单页面应用;
- main.js:入口js文件,导入影响全局的公共库;
1.5vue组件的代码编写
1.5.1vue组件模板的样式
<template>
</template>
<script>
export default {
name: "test"
}
</script>
<style scoped>
</style>
- 其中<template></template>是编写页面展示样式的代码,包括了背景颜色,需要的表单、按钮、下拉框等组件,如登录界面的代码实例如下(根据界面设计中各组件的包含归属关系合理编写代码):
<template>
<div style="width: 100%;height: 100vh;background-color: greenyellow;overflow: hidden">
<div style="width: 400px;margin: 150px auto">
<div style="color: #cccccc;font-size: 30px;text-align: center">欢迎登录</div>
<!-- 这里的:model="form"很重要,不然后面的$['form']无法使用-->
<el-form :model="form" ref="form" size="default" :rules="rules">
<el-form-item prop="username">
<el-input prefix-icon="User" v-model="form.username" >
</el-input>
</el-form-item >
<el-form-item prop="password">
<el-input prefix-icon="Lock" v-model="form.password" ></el-input>
</el-form-item>
<el-form-item>
<el-button style="width: 100%" type="primary" @click="login">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script></script>部分用于编写JavaScript代码,对<template>中涉及到的数据、事件等函数编写逻辑代码,如数据函数部分:
export default {
name: "LoginView",
data(){
return {
form:{},
rules:{
username:[//与前面绑定的元素,作用的元素在名字上一一对应
{required:true,message:'请输入用户名',trigger:'blur'},
],
password:[//与前面绑定的元素,作用的元素在名字上一一对应
{required:true,message:'请输入密码',trigger:'blur'},
],
}
}
},
}
- 逻辑函数以及事件函数部分:
methods:{
login(){
this.$refs['form'].validate((valid)=>{
if(valid){
request.post("/api/user/login",this.form).then(res=>{
if (res.code === '0') {
this.$message({
type: "success",
message: "登录成功"
})
this.$router.push("/")//登录后跳转
} else {
this.$message({
type: "error",
message: res.msg
})
}
})
}
})
}
}
}
注:箭头函数相当于匿名函数,并且简化了函数定义。箭头函数指向函数内部this已经绑定了外部的vue实例。
1.6 layout.vue
<template>
<div>
<Header/>
<div style="display: flex">
<Aside/>
<router-view style="flex: 1"/>
</div>
</div>
</template>
<script>
import Header from "@/components/Header";
import Aside from "@/components/Aside";
export default {
name: "Layout",
components:{
Header,
Aside
}
}
</script>
<style scoped>
</style>
1.7router 文件夹中的路由
- 针对每一个页面组织他们的路由关系,如果有多个views组件的通用components组件,那么可以将整合的layout组件放置在父组件中,然后在引入children组件:
const routes = [
{
path: '/',
name: 'Layout',
component: Layout,
redirect:"/home",
children:[
{
path: 'home',
name: 'HomeView',
component: ()=>import("@/views/HomeView"),
},
{
path: 'book',
name: 'BookView',
component: ()=>import("@/views/BookView"),
},
]
},
]
- 剩余的路由组件如下:
{
path: '/login',
name: 'LoginView',
component: ()=>import("@/views/LoginView")
},
{
path: '/register',
name: 'RegistView',
component: ()=>import("@/views/RegistView")
},
1.8utils中引入的工具包
如使用axios中的request拦截器和respose拦截器,为前后端搭建一个数据传递桥梁:
import axios from 'axios'
const request = axios.create({
timeout: 5000
})
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === 'blob') {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log('err' + error) // for debug
return Promise.reject(error)
}
)
export default request
1.9在vue.config.js中设置代理,解决前后端跨域传递数据的问题
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
module.exports = {
devServer: { //记住,别写错了devServer//设置本地默认端口 选填
port: 9876,
proxy: { //设置代理,必须填
'/api': { //设置拦截器 拦截器格式 斜杠+拦截器名字,名字可以自己定
target: 'http://localhost:9090', //代理的目标地址
changeOrigin: true, //是否设置同源,输入是的
pathRewrite: { //路径重写
'/api': '' //选择忽略拦截器里面的单词
}
}
}
}
}
1.10main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/assets/css/global.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
createApp(App).use(store).use(router).use(ElementPlus,{locale: zhCn,size:'small'}).mount('#app')
1.11项目加载的过程
public/index.html->main.js->app.vue->router/index.js->x.vue
二、springboot及后端项目搭建
2.1利用spring.Initializer初始化springboot项目
- https://start.spring.io
2.2springboot目录结构
src->main->java
- commen公共资源,如分页插件代码和返回提示代码:
package com.example.demo.common;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus 分页插件
*/
@Configuration
@MapperScan("com.example.demo.mapper")
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
package com.example.demo.common;
public class Result<T> {
private String code;
private String msg;
private T data;//数据泛型
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Result() {
}
public Result(T data) {
this.data = data;
}
public static Result success() {
Result result = new Result<>();
result.setCode("0");
result.setMsg("成功");
return result;
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>(data);
result.setCode("0");
result.setMsg("成功");
return result;
}
public static Result error(String code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
- entity文件夹定义实体代码
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("user")
@Data
public class User {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String username;
private String nickName;
private String password;
private Integer age;
private String sex;
private String address;
}
- mapper文件夹定义一些连接数据库的接口
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
public interface UserMapper extends BaseMapper<User> {
}
- controller文件夹定义数据控制函数,比如数据的增删改查功能
package com.example.demo.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.Result;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user")
public class Usercontroller {
@Resource
UserMapper userMapper;
@PostMapping
public Result<?> save(@RequestBody User user){
if(user.getPassword()==null){
user.setPassword("123456");
}
userMapper.insert(user);
return Result.success();
}
@PostMapping("/login")
public Result<?> login(@RequestBody User user){
User res=userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername,user.getUsername()).eq(User::getPassword,user.getPassword()));
if(res==null){
return Result.error("-1","用户信息或密码错误");
}
return Result.success();
}
@DeleteMapping("/{id}")
//这里需要有Pathvariable注解,才能实现rest风格的下的路径占位匹配
public Result<?> delete(@PathVariable Long id){
userMapper.deleteById(id);
return Result.success();
}
@PutMapping
public Result<?> update(@RequestBody User user){
userMapper.updateById(user);
return Result.success();
}
@GetMapping
public Result<?> findPage(@RequestParam(defaultValue = "1")Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(defaultValue = "") String search){
LambdaQueryWrapper<User> wrapper =Wrappers.<User>lambdaQuery();
if(StrUtil.isNotBlank(search)){
wrapper.like(User::getNickName,search);
}
Page<User> userPage=userMapper.selectPage(new Page<>(pageNum,pageSize), wrapper);
return Result.success(userPage);//这里的返回值是userPage为什么?
}
//登录和注册都是发送,要用post
@PostMapping("/register")
public Result<?> register(@RequestBody User user){//获取后要得到一个反馈
User res=userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername,user.getUsername()));
if(res!=null) {
return Result.error("-1","用户名重复");
}
if(user.getPassword()==null) {
user.setPassword("123456");
}
userMapper.insert(user);
return Result.success();
}
}
scr->main->resources
- application.properties(设置数据库的连接配置)
server.port=9090
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot-vue?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
spring.datasource.username=root
spring.datasource.password=239123
2.3maven项目的配置文件pom.xml
用于加载需要的依赖包和库
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
注:需要注意,在引入需要的依赖包和依赖文件有版本的限制问题,包和包之间需要版本的合理搭配,不然无法使用或者会报一些意想不到的错误。
三、数据库
数据库使用Mysql,具体的安装和配置略,建表实例如下: