从0到1 前后分离项目开发实战
2021/12/20 23:24:14
本文主要是介绍从0到1 前后分离项目开发实战,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前后端分离项目开发
文章目录
- 前后端分离项目开发
- 前言
- 技术栈
- 前端
- 后端
- 数据库
- 安装 前端环境
- node.js 查看是否安装成功
- npm 查看是否安装成功
- 安装vue vue-cli 脚手架
- 创建vue项目
- 1)Book.vue
- 2)index.js配置
- 3)测试
- 显示数据
- Book.vue
- 永远的CRUD
- 安装axios
- 安装element-ui
- 开发步骤
- (1)创建数据库 test 和表 student
- (2)构建springboot项目 并按照mvc模式开发好 curd接口,使用postman发送请求保证每个接口正常执行
- (3)前端页面对应操作放置 对应的后端接口请求。
- Show
前言
前后端分离就是一个应用的前端代码和后端代码分开写,为什么这么做?
如果不使用前后端分离的方式,会有那些问题?
传统java web开发中,前端使用JSP开发,后端使用servlet。
前端 — 》HTML 静态文件 --》 后端 – 》 JSP
这种开发方式效率极低,可以使用前后端分离的方式进行开发,就可以完
美解决这一问题。
前端只需要独立编写客户端代码,后端也只需要独立编写服务端代码提供
数据接口即可。前端通过Ajax请求来访问后端的数据接口,将Model展示
到View中即可。
前后端开发者只需要提前约定好接口文档(URL、参数、数据类型…),然
后分别独立开发即可,前端可以造假数据进行测试,完全不需要依赖于后
端。完全不依赖于后端,前后端相互独立。前后端应用解耦,极大提升了开发效率。
单体 – 》 前端应用 + 后端应用
前端应用 : 负责数据展示和用户交互
后端应用: 负责提供数据处理接口。
前端HTML --》 Ajax --》 Restful 后端开发
技术栈
前端
1.vue.js
2.axios
3.element-ui
后端
1.springboot
2.mybatis-plus
数据库
mysql
安装 前端环境
node.js 查看是否安装成功
node -v
没有就得安装
安装淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm 查看是否安装成功
npm -v
安装vue vue-cli 脚手架
cnpm install vue-cli 或者安装最新版 cnpm i -g @vue/cli
查看 vue-cli是否安装成功
vue --version
可能出现的问题是
’Set-ExecutionPolicy’ 不是内部或外部命令,也不是可运行的程序
解决办法
用管理员的身份打开 power shell,执行以下命令:
set-ExecutionPolicy RemoteSigned get-ExecutionPolicy
创建vue项目
vue ui
可能出现的问题
vue ui 命令弹出空白页面
解决办法
在Chrome浏览器的设置中允许所有cookie
解决后执行命令
vue ui
出现的页面是如下所示:
使用vscode打开创建好的vue项目;
1)Book.vue
在 src/views 下创建 Book.vue
<template> <div> <table> <tr> <td>编号</td> <td>书名</td> <td>作者</td> </tr> <tr> {{msg}} </tr> </table> </div> </template> <script> export default { name: "Book", data(){ return{ msg: 'Hello' } } } </script> <style scoped> </style>
2)index.js配置
打开 src/router 下的 index.js
index.js
- 导入Book.vue
import Book from “…/views/Book”
- 配置(一定要在前带 逗号(,) )
,{ path: ‘/book’, component: Book }
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import Book from '../views/Book.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') }, { path: '/book', name: 'Book', component: Book } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
3)测试
Terminal 中输入 npm run serve
访问
显示数据
Book.vue
-
遍历
books:被遍历的数组 ,item:每次遍历的对象
< tr v-for="item in books ">
< td >{{item.id}}< /td >
< td >{{item.name}}< /td >
< td >{{item.author}}< /td >
< /tr > -
books中添加数据
books: [ { id: 1, name: ‘Java’, author: ‘哈哈’ }, { id: 2, name: ‘C++’, author: ‘啦啦’ }, { id: 3, name: ‘Python’, author: ‘嘿嘿’ } ]
<template> <div> <table> <tr> <td>编号</td> <td>书名</td> <td>作者</td> </tr> <!--books:被遍历的数组 ,item:每次遍历的对象--> <tr v-for="item in books"> <td>{{item.id}}</td> <td>{{item.name}}</td> <td>{{item.author}}</td> </tr> </table> </div> </template> <script> export default { name: "Book", data() { return { msg: 'Hello', books: [ { id: 1, name: 'Java', author: '哈哈' }, { id: 2, name: 'C++', author: '啦啦' }, { id: 3, name: 'Python', author: '嘿嘿' } ] } } } </script> <style scoped> </style>
永远的CRUD
从上面的demo可以看出,数据是在前端页面构造的,而不是数据库获取的数据。因此,需要前端发送ajax请求得到数据.
安装axios
vue add axios
查看是否安装成功
安装element-ui
1. 在项目下 输入 npm install element-ui -S
2.查看配置文件package.json,是否有element-ui组件的版本号 如下图
4.在main.js文件中 引入 element 组件 :
import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI)
5.使用elementui组件开发Student.vue页面
element-ui
demo展示
6.此时使用axios发送http请求访问后台页面数据会因为浏览器的同源策略造成跨域问题
解决办法
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET","HEAD","POST","DELETE","OPTIONS","PUT") .allowCredentials(true) .maxAge(3600) .allowedHeaders("*"); } }
开发步骤
(1)创建数据库 test 和表 student
CREATE DATABASE /*!32312 IF NOT EXISTS*/`test` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `test`; /*Table structure for table `student` */ DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'ID自增', `name` varchar(20) DEFAULT NULL COMMENT '姓名', `gender` tinyint(1) DEFAULT NULL COMMENT '性别 0女1男', `birthday` date DEFAULT NULL COMMENT '生日', `headImageFilePath` varchar(100) DEFAULT NULL COMMENT '头像路径 学生头像相对路径', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8mb4; /*Data for the table `student` */ insert into `student`(`id`,`name`,`gender`,`birthday`,`headImageFilePath`) values (3,'wang',0,'2002-03-21','https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'), (25,'cs',1,'2021-12-15','https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'), (49,'15',0,'2021-12-14','');
(2)构建springboot项目 并按照mvc模式开发好 curd接口,使用postman发送请求保证每个接口正常执行
项目结构
(3)前端页面对应操作放置 对应的后端接口请求。
for example
后端对应
@RequestMapping("/selectAll") public List<Student> selectAll(){ return studentService.selectAll(); }
由前台页面发送查询请求
loadData(){ const _this = this; axios.get('http://127.0.0.1:8081/selectAll').then(function(resp){ _this.students = resp.data; }).catch((err) =>{ console.log(err); }); },
源码 :页面Student.vue
<template> <div> <el-dialog title="学生信息" :visible.sync="dialogFormVisible"> <el-form :model="form"> <el-form-item label="名称" :label-width="formLabelWidth"> <el-input v-model="form.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="性别" :label-width="formLabelWidth"> <el-select v-model="form.gender" placeholder="请选择性别"> <!-- 通过循环的形式展示出下拉菜单 key必须添加,否则可能会出错,相当于唯一性标识 --> <el-option v-for="sextype in sexType" :key="sextype.type" :label="sextype.name" :value="sextype.type"> </el-option> </el-select> </el-form-item> <el-form-item label="生日" :label-width="formLabelWidth"> <el-date-picker type="date" placeholder="日期" value-format="yyyy-MM-dd" v-model="form.birthday"></el-date-picker> </el-form-item> <el-form-item label="头像" :label-width="formLabelWidth"> <!-- <el-input v-model="form.headImageFilePath"></el-input> --> <!-- 文件上传 --> <single-upload v-model="form.headImageFilePath"></single-upload> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="cancel">取 消</el-button> <el-button type="primary" @click="update">确 定</el-button> </div> </el-dialog> <el-form :model="queryParam" ref="form" label-width="100px" class="demo-ruleForm" size="mini"> <el-row> <el-col :span="8"> <el-form-item label="姓名"> <el-input v-model="queryParam.name"></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="性别"> <el-select v-model="queryParam.gender" placeholder="性别"> <el-option label="男" value="1"></el-option> <el-option label="女" value="0"></el-option> </el-select> </el-form-item> </el-col> <el-col :span="8"> <el-form-item> <el-button type="primary" @click="add">新增</el-button> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="16"> <el-form-item label="生日"> <el-date-picker type="date" placeholder="开始日期" value-format="yyyy-MM-dd" v-model="queryParam.startDate"></el-date-picker> -- <el-date-picker type="date" placeholder="结束日期" value-format="yyyy-MM-dd" v-model="queryParam.endDate" ></el-date-picker> </el-form-item> </el-col> <el-col :span="8"> <el-form-item> <el-button type="primary" @click="searchQuery" icon="el-icon-search">查询</el-button> <el-button type="warning" @click="resetForm" icon="el-icon-search" plain>重置</el-button> </el-form-item> </el-col> </el-row> </el-form> <el-table :data="students" style="width: 100%"> <el-table-column prop="id" label="ID" v-if="false" width="180"> </el-table-column> <el-table-column prop="headImageFilePath" label="头像" width="180"> <template slot-scope="scope"> <img :src="scope.row.headImageFilePath" min-width="70" height="70" /> </template> </el-table-column> <el-table-column prop="name" label="姓名" width="180"> </el-table-column> <el-table-column prop="gender" label="性别" width="180"> <template slot-scope="scope"> <!-- 通过 Scoped slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据 用|分割,前面获取传给后面,然后通过filters获取 --> <span>{{scope.row.gender | sexTypeFilter}}</span> </template> </el-table-column> <el-table-column prop="birthday" value-format="yyyy-MM-dd" label="生日"> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button> </template> </el-table-column> </el-table> </div> </template> <script> // @ is an alias to /src import SingleUpload from '@/components/upload/singleUpload.vue' // 过滤器的数据写在data外面,因为过滤器不能调用this. const sexType = [ { "type":1, "name":'男'}, { "type":0, "name":'女'} ] export default { name: "Student", components:{ SingleUpload }, data(){ return { sexType, input: '', students: [ ], fits: ['fill'], url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', imageUrl: '', dialogFormVisible: false, formLabelWidth: '80px', flag: '', // 因为编辑和新增共用一个弹框元素,因此用flag加以区分 form: { name: '', gender: '', birthday:'', headImageFilePath:'' }, queryParam: { name: '', gender: '', birthday:'', starDate: '', endDate: '' } } }, //过滤器,数字类型和汉字的转换 //两种写法都可以,type是传入的数字,与过滤器数据对比 // sexType.find(obj=>{ // return obj.type === type}) filters:{ sexTypeFilter(type){ const sexTy = sexType.find(obj => obj.type === type) return sexTy ? sexTy.name : null }, }, created(){ // this.loadData(); this.searchQuery(); }, methods: { loadData(){ const _this = this; axios.get('http://127.0.0.1:8081/selectAll').then(function(resp){ _this.students = resp.data; }).catch((err) =>{ console.log(err); }); }, add(){ this.flag = 1; this.form = { name: '', gender: '', birthday:'', headImageFilePath:'' }, // 设置点击按钮之后进行显示对话框 this.dialogFormVisible = true; }, cancel(){ this.dialogFormVisible = false; }, update(){ if(this.flag == 1){ // 将我们添加的信息提交到总数据里面 this.students.push(this.form); this.save(); }else{ this.updateOne(); } this.dialogFormVisible = false; }, save(){ axios.post('http://127.0.0.1:8081/add',this.form).then((resp) =>{ }).catch((err) =>{ console.log(err); }); }, updateOne(){ var s = this.form; axios.post('http://127.0.0.1:8081/update',this.form).then((resp) =>{ }).catch((err) =>{ console.log(err); }); }, searchQuery(){ var that = this; axios.post('http://127.0.0.1:8081/selectAllbyCondition',this.queryParam).then((resp) =>{ that.students = resp.data; }).catch((err) =>{ console.log(err); }); }, resetForm(){ this.flag = ''; this.queryParam = {}; }, handleEdit(index, row) { this.flag = 2; // 将数据的index传递过来用于实现数据的回显 this.form = this.students[index]; // 设置对话框的可见 this.dialogFormVisible = true; }, handleDelete(index, row) { var id = row.id; axios.delete('http://127.0.0.1:8081/delete'+'/'+id) .then(() => { this.students.splice(index, 1); this.$message({ type: "success", message: "删除成功!" }); }).catch((err) =>{ console.log(err); }); } }, } </script> <style> </style>
这里面有很多细节,需要读者实操体会
项目结构
单文件上传 singleUpload.vue
<template> <el-upload class="avatar-uploader" action="https://jsonplaceholder.typicode.com/posts/" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" > <img v-if="imageUrl" :src="imageUrl" class="avatar" /> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </template> <script> export default { data() { return { imageUrl: '', file:'', headers: { 'Content-Type': 'multipart/form-data', // 默认值 }, // 图片上传参数 // actionUrl: this.$axios.defaults.baseURL + '/file/uploadImage', } }, methods: { handleAvatarSuccess(res, file) { debugger; this.imageUrl = URL.createObjectURL(file.raw) }, beforeAvatarUpload(file) { debugger; var s = file; const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg' const isLt2M = file.size / 1024 / 1024 < 2 if (!isJPG) { this.$message.error('上传头像图片只能是 JPG 格式!') } if (!isLt2M) { this.$message.error('上传头像图片大小不能超过 2MB!') } return isJPG && isLt2M }, }, } </script> <style> .avatar-uploader .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; } .avatar-uploader .el-upload:hover { border-color: #409eff; } .avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 178px; height: 178px; line-height: 178px; text-align: center; } .avatar { width: 178px; height: 178px; display: block; } </style>
Show
页面加载
编辑
添加
条件查询
删除看不到效果就不展示了。
这篇关于从0到1 前后分离项目开发实战的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15JavaMailSender是什么,怎么使用?-icode9专业技术文章分享
- 2024-11-15JWT 用户校验学习:从入门到实践
- 2024-11-15Nest学习:新手入门全面指南
- 2024-11-15RestfulAPI学习:新手入门指南
- 2024-11-15Server Component学习:入门教程与实践指南
- 2024-11-15动态路由入门:新手必读指南
- 2024-11-15JWT 用户校验入门:轻松掌握JWT认证基础
- 2024-11-15Nest后端开发入门指南
- 2024-11-15Nest后端开发入门教程
- 2024-11-15RestfulAPI入门:新手快速上手指南