服务器2003系统如何建设网站,公司起名网站,浙江壹设软装设计有限公司,更换模板对网站seo的影响前后端分离简介
前后端分离
前后端分离就是将⼀个应⽤的前端代码和后端代码分开写#xff0c;为什么要这样做#xff1f;
如果不使⽤前后端分离的⽅式#xff0c;会有哪些问题#xff1f;
传统的 Java Web 开发中#xff0c;前端使⽤ JSP 开发#xff0c;JSP 不是由后…前后端分离简介
前后端分离
前后端分离就是将⼀个应⽤的前端代码和后端代码分开写为什么要这样做
如果不使⽤前后端分离的⽅式会有哪些问题
传统的 Java Web 开发中前端使⽤ JSP 开发JSP 不是由后端开发者来独⽴完成的。
前端—》HTML 静态⻚⾯ —〉后端 —》JSP
这种开发⽅式效率极低可以使⽤前后端分离的⽅式进⾏开发就可以完美地解决这⼀问题。
前端只需要独⽴编写客户端代码后端也只需要独⽴编写服务端代码提供数据接⼝即可。
前端通过 Ajax 请求来访问后端的数据接⼝将 Model 展示到 View 中即可。
前后端开发者只需要提前约定好接⼝⽂档URL、参数、数据类型…然后分别独⽴开发即可前端
可以造假数据进⾏测试完全不需要依赖于后端最后完成前后端集成即可真正实现了前后端应⽤的
解耦合极⼤地提升了开发效率。
单体—》前端应⽤ 后端应⽤
前端应⽤负责数据展示和⽤户交互。
后端应⽤负责提供数据处理接⼝。
前端 HTML —》Ajax —〉RESTful 后端数据接⼝。
传统的单体应⽤
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VkFL4q5b-1610345829474)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20210111112048520.png)]
前后端分离的结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q5usU00y-1610345829478)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20210111112106144.png)]
前后端分离就是将⼀个单体应⽤拆分成两个独⽴的应⽤前端应⽤和后端应⽤以 JSON 格式进⾏数据交互。
实现技术
Spring Boot Vue
使⽤ Spring Boot 进⾏后端应⽤开发使⽤ Vue 进⾏前端应⽤开发。
Vue Element UI
Vue 集成 Element UI
Element UI 后台管理系统主要的标签
el-container构建整个⻚⾯框架。
el-aside构建左侧菜单。
el-menu左侧菜单内容常⽤属性
:default-openeds默认展开的菜单通过菜单的 index 值来关联。
:default-active默认选中的菜单通过菜单的 index 值来关联。
el-submenu可展开的菜单常⽤属性
index菜单的下标⽂本类型不能是数值类型。
template对应 el-submenu 的菜单名。
i设置菜单图标通过 class 属性实则。
el-icon-messae
el-icon-menu
el-icon-setting
el-menu-item菜单的⼦节点不可再展开常⽤属性
index菜单的下标⽂本类型不能是数值类型。
Vue router 来动态构建左侧菜单
导航1
⻚⾯1
⻚⾯2
导航2
⻚⾯3
⻚⾯4
menu 与 router 的绑定
1、 标签添加 router 属性。
2、在⻚⾯中添加 标签它是⼀个容器动态渲染你选择的 router。
3、 标签的 index 值就是要跳转的 router。
Element UI 表单数据校验
定义 rules 对象在 rules 对象中设置表单各个选项的校验规则
required: true, 是否为必填项
message: ‘error’, 提示信息
trigger: ‘blur’触发事件
Demo最终效果 项目结构 环境准备
jdk 1.8maven 3.6.3IntelliJ IDEA 2019.3 安装vue.js插件 SpringBoot *2.2.2* .RELEASE vue/cli 4.2.2 ( node.js) Vue CLI3开发环境搭建 新建Vue工程 命令行输入vue ui ( vue3版本才有) 项目创建完成后添加插件element UI 用IDEA打开刚才创建的Vue项目在IDEA的命令行终端输入npm run serve启动Vue ( ctrl c 停止vue ) 即可访问项目首页 http://localhost:8080 三、新建Spring Boot工程
创建Spring Boot工程需要的组件 Lombok用于自动生成各种属性的 Setter 和 Getter 方法省去手动生成。在数据库library中创建book表
DROP TABLE IF EXISTS book;
/*!40101 SET saved_cs_client character_set_client */;SET character_set_client utf8mb4 ;
CREATE TABLE book (id int(10) NOT NULL AUTO_INCREMENT,name varchar(20) DEFAULT NULL,author varchar(20) DEFAULT NULL,publish varchar(20) DEFAULT NULL,pages int(10) DEFAULT NULL,price float(10,2) DEFAULT NULL,bookcaseid int(10) DEFAULT NULL,abled int(10) DEFAULT NULL,PRIMARY KEY (id),
) ENGINEInnoDB AUTO_INCREMENT119 DEFAULT CHARSETutf8;
/*!40101 SET character_set_client saved_cs_client */;--
-- Dumping data for table book
--LOCK TABLES book WRITE;
/*!40000 ALTER TABLE book DISABLE KEYS */;
INSERT INTO book VALUES (1,解忧杂货店,东野圭吾,电子工业出版社,102,27.30,9,1),
(2,追风筝的人,卡勒德·胡赛尼,中信出版社,330,26.00,1,1),
(3,人间失格,太宰治,作家出版社,150,17.30,1,1),
(4,这就是二十四节气,高春香,电子工业出版社,220,59.00,3,1),
(5,白夜行,东野圭吾,南海出版公司,300,27.30,4,1),
(6,摆渡人,克莱儿·麦克福尔,百花洲文艺出版社,225,22.80,1,1),
(7,暖暖心绘本,米拦弗特毕,湖南少儿出版社,168,131.60,5,1),
(8,天才在左疯子在右,高铭,北京联合出版公司,330,27.50,6,1),
(9,我们仨,杨绛,生活.读书.新知三联书店,89,17.20,7,1),
(10,活着,余华,作家出版社,100,100.00,6,1),
(11,水浒传,施耐庵,三联出版社,300,50.00,1,1),
(12,三国演义,罗贯中,三联出版社,300,50.00,2,1),
(13,红楼梦,曹雪芹,三联出版社,300,50.00,5,1),
(14,西游记,吴承恩,三联出版社,300,60.00,3,1);
/*!40000 ALTER TABLE book ENABLE KEYS */;
UNLOCK TABLES;删除mould。。。本地从新安装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-46CTfKkF-1610345829481)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20210110170030932.png)] 根据Element官网给出的样式实例代码进行相关修改 修改后的App.Vue 如下
templatediv idappel-container styleheight: 500px; border: 1px solid #eeeel-aside width200px stylebackground-color: rgb(238, 241, 246)el-menu :default-openeds[1, 3]el-submenu index1template slottitlei classel-icon-message/i导航一/templateel-menu-item-grouptemplate slottitle分组一/templateel-menu-item index1-1选项1/el-menu-itemel-menu-item index1-2选项2/el-menu-item/el-menu-item-group/el-submenu /el-menu/el-asideel-mainrouter-view/router-view/el-main/el-container/div
/templatestyle.el-header {background-color: #B3C0D1;color: #333;line-height: 60px;}.el-aside {color: #333;}
/stylescriptexport default {data() {const item {date: 2016-05-02,name: 王小虎,address: 上海市普陀区金沙江路 1518 弄};return {tableData: Array(20).fill(item)}}};
/script新建index.vue
templaterouter-view/router-view
/templatescriptexport default {name: Index}
/scriptstyle scoped/styleApp.Vue 实现整体框架侧边栏每次页面跳转的时候这个框架的样式是不变的.App.vue 中的router-view 套index index 的router-view 套其他界面这样才能保证侧边栏一直存在。此后的每次页面跳转都只更改index的router-view中的内容 menu 与 router的绑定实现页面跳转
新建前端页面
在views中新建
BookManager.vue (查询图书页面)、BookUpdate.vue图书信息修改页面、AddBook.vue添加图书页面
配置路由router/index.js )
import Vue from vue
import VueRouter from vue-router
import Index from ../views/Index;
import BookManager from ../views/BookManager;
import AddBook from ../views/AddBook;
import BookUpdate from ../views/BookUpdate;Vue.use(VueRouter)const routes [{path: /,name: 图书管理,show: true,component: Index,/* 首页地址直接跳转到/BookManager */redirect: /BookManager,children: [{path: /BookManager,name: 查询图书,component: BookManager},{path: /AddBook,name: 添加图书,component: AddBook},]},{path: /BookUpdate,// name: 修改图书,component: BookUpdate,show: false//开始时候先不显现出来}]const router new VueRouter({mode: history,base: process.env.BASE_URL,routes
})export default router
修改App.vue, 实现menu 与 router的绑定页面跳转
标签添加router属性在index页面中添加route-view标签他是一个容器动态渲染你选择的routerel-menu-item标签的index值就是要跳转的router
templatediv idappel-container styleheight: 700px; border: 1px solid #eeeel-aside width200px stylebackground-color: rgb(238, 241, 246)el-menu router :default-openeds[0, 1]!--循环遍历routes中的对象有几个对象就有几个item--!--添加index属性标识不同的submenu--!--默认的index是字符串类型而不是整数类型多以需要转化为字符类型--!--v-ifitem.show 表示根据路由配置中的show:ture/false决定是否显示该菜单 --el-submenu v-for(item,index) in $router.options.routes :indexindex v-ifitem.showtemplate slottitlei classel-icon-setting/i{{item.name}}/templateel-menu-item v-for(item2,index2) in item.children :indexitem2.path:class$route.pathitem2.path?is-active:{{item2.name}}/el-menu-item/el-submenu/el-menu/el-asideel-mainrouter-view/router-view/el-main/el-container/div
/template前后端信息传递显示图书信息
JPA 连接数据库配置
application.yml
spring:datasource:# 连接数据库libraryurl: jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf-8serverTimezoneUTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverjpa:# 打印sqlshow-sql: trueproperties:hibernate:# 格式化sqlformat_sql: trueserver:port: 8181vue已经默认占用了8080端口修改springboot的端口为8181防止冲突
整合SpringData JPA, 编写一个实体类bean和数据表进行映射
在spring boot项目中新建实体类 entity/Book.java
/*** 使用Entity将该类与数据库表绑定根据类名和表名经行绑定默认类名小写就是表名;** Data是lombok插件的方法自动帮我们生成各种setter和getter方法*/
Entity
Data
public class Book {// 和数据库表中的id绑定表示这是主键Id// 设置主键Id自增GeneratedValue(strategy GenerationType.IDENTITY)private Integer id;private String name;private String author;private String publish;private Integer pages;private Integer price;
}编写一个Dao接口来操作实体类对应的数据表Repository
新建 repository/ BookRepository.java
Spring Data 提供了统一的 repository 接口实现了数据库的相关基本操作 继承 JpaRepository 来完成对数据库的操作
public interface BookRepository extends JpaRepositoryBook, Integer {}新建Controller层处理浏览器请求
controller/ BookHandler.java
/*** RestController Controller ResponseBody* Controller 将当前修饰的类注入SpringBoot IOC容器使得从该类所在的项目跑起来的过程中这个类就被实例化。* 当然也有语义化的作用即代表该类是充当Controller的作用* ResponseBody 该类中所有的API接口返回的数据都会以Json字符串的形式返回给客户端** RequestMapping 提供路由信息负责URL到Controller中的具体函数的映射。服务器发送 /book 请求时执行该类** 通过Handler数据才能调给前端使用*/
RestController
RequestMapping(/book)
public class BookHandler {Autowiredprivate BookRepository bookRepository;/*** 分页显示** GetMapping是一个组合注解 是RequestMapping(method RequestMethod.GET)的缩写* page/size 表示从第几页开始每页几个*/GetMapping(/findAll/{page}/{size})public PageBook findAll(PathVariable(page) Integer page, PathVariable(size) Integer size){PageRequest request PageRequest.of(page,size);return bookRepository.findAll(request);}
}运行该项目
http://localhost:8181/book/findAll/0/6 表示从第0页开始显示6个数据
前端发送Ajax请求8181端口获取后端数据 通过 axios 组件请求 Ajax axios 是目前应用最为广泛的 Ajax 封装库 终止vue服务后 在命令行输入vue add axios 安装axios 修改 BookManager.vue
created() 方法页面一打开就会调用page( 方法实现点击页数跳转 el-table-columnfixedrightlabel操作width100template slot-scopescopeel-button clickedit(scope.row) typetext sizesmall修改/el-buttonel-button clickdeleteBook(scope.row) typetext sizesmall删除/el-button!--delete是关键字不能自定义--/template/el-table-column el-paginationbackgroundlayoutprev, pager, next:page-sizepageSize:totaltotalcurrent-changepage!--点击页数跳转--/el-paginationscriptexport default {methods: {page(currentPage){const _this this!--通过axios发送ajax 请求后端数据--axios.get(http://localhost:8181/book/findAll/(currentPage-1)/10).then(function(resp){console.log(resp)_this.tableData resp.data.content_this.pageSize resp.data.size_this.total resp.data.totalElements})}},data() {return {pageSize: ,total: ,tableData: []}},created(){const _this thisaxios.get(http://localhost:8181/book/findAll/0/10).then(function(resp){console.log(resp)_this.tableData resp.data.content_this.pageSize resp.data.size_this.total resp.data.totalElements})}}
/script访问 http://localhost:8080/BookManager (刷新浏览器) 查看控制台出现跨域问题前端无法获取后端数据
6. 解决跨域问题
在后端解决跨域问题 新建配置类 config/ CrosConfig.java
/*** 解决跨域问题*/
Configuration
public class CrosConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping(/**).allowedOrigins(*).allowedMethods(GET, HEAD, POST, PUT, DELETE, OPTIONS).allowCredentials(true).maxAge(3600).allowedHeaders(*);}
}修改
流程
前端主页面点击修改按钮后端根据id查询此图书信息 findById前端发送ajax请求获取数据并跳转到修改页面 BookUpdate.vue前端传递修改后的信息给后端update函数处理返回主页面
BookHandler.hava/*** 根据Id查询图书信息*/GetMapping(/findById/{id})public Book findById(PathVariable(id) Integer id){return bookRepository.findById(id).get();}/*** 修改图书信息** PUT请求如果两个请求相同后一个请求会把第一个请求覆盖掉。所以PUT用来改资源* Post请求后一个请求不会把第一个请求覆盖掉。所以Post用来增资源*/PutMapping(/update)public String update(RequestBody Book book){Book result bookRepository.save(book);if(result ! null){return success;}else{return error;}}BookUpdate.vue核心代码 el-form-itemel-button typeprimary clicksubmitForm(ruleForm)修改/el-buttonel-button clickresetForm(ruleForm)重置/el-button/el-form-itemscriptexport default {data() {return {// 数据ruleForm: {id: ,name: ,author: ,publish: ,pages: ,price: ,},//校验规则rules: {name: [{ required: true, message: 图书名称不能为空, trigger: blur }],author: [{ required: true, message: 作者姓名不能为空, trigger: blur }],publish: [{ required: true, message: 出版社不能为空, trigger: blur }],price: [{ required: true, message: 价格不能为空, trigger: blur }],}};},methods: {submitForm(formName) {const _this thisthis.$refs[formName].validate((valid) {if (valid) {/*put和后端的PutMapping对应*/axios.put(http://localhost:8181/book/update, this.ruleForm).then(function(resp){if(resp.data success){// 消息弹出框---- element官网找个模板直接复制_this.$alert(《 _this.ruleForm.name 》修改成功, 提示, {confirmButtonText: 确定,callback: action {// 跳转界面_this.$router.push(/BookManager)}});}})} else {return false;}});},resetForm(formName) {this.$refs[formName].resetFields();}},created() {const _this thisaxios.get(http://localhost:8181/book/findById/ this.$route.query.id ).then(function(resp){_this.ruleForm resp.data})}}
/script添加
BookHandler.java/*** 添加图书** RequestBody 通过映射把前端Json数据转化成java对象*/PostMapping(/add)public String save(RequestBody Book book){Book result bookRepository.save(book);if(result ! null){return success;}else{return error;}}AddBook.vue
el-form-itemel-button typeprimary clicksubmitForm(ruleForm)提交/el-buttonel-button clickresetForm(ruleForm)重置/el-button
/el-form-itemmethods: {submitForm(formName) {const _this thisthis.$refs[formName].validate((valid) {if (valid) {axios.post(http://localhost:8181/book/add, this.ruleForm).then(function(resp){if(resp.data success){// 消息弹出框---- element官网找个模板直接复制_this.$alert(《 _this.ruleForm.name 》添加成功, 提示, {confirmButtonText: 确定,callback: action {// 跳转界面_this.$router.push(/BookManager)}});}})} else {return false;}});},resetForm(formName) {this.$refs[formName].resetFields();}}
删除
删除之后直接返回主界面 BookHandler.java DeleteMapping(/delete/{id})public void delete(PathVariable(id) Integer id){bookRepository.deleteById(id);}BookManger.vue
template slot-scopescopeel-button clickedit(scope.row) typetext sizesmall修改/el-buttonel-button clickdeleteBook(scope.row) typetext sizesmall删除/el-button!--delete是关键字不能自定义--
/templatedeleteBook(row){const _this thisaxios.delete(http://localhost:8181/book/delete/ row.id).then(function(resp){// 消息弹出框---- element官网找个模板直接复制_this.$alert(《 row.name 》删除成功, 提示, {confirmButtonText: 确定,callback: action {// 动态刷新此页面window.location.reload()}});})},