IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 前后端分离与vue+springboot实现基本的crud操作 -> 正文阅读

[JavaScript知识库]前后端分离与vue+springboot实现基本的crud操作

作者:https://img-blog.csdnimg.cn/2021071522041689.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0VzdHhpeWFuZw==,size_16,color_FFFFFF,t_70#pic_center

前后端分离

1.将应用的前后端代码分开写

前后端分离的背景

  1. 传统的javaweb开发前端使用jsp开发,jsp页面是由前端工作人员学出—>html静态页面---->后端—>jsp,当前端出现问题时,页面就要再交付给前端,而前端看到的东西已经不是之前看到的了,从而导致冲突,影响开发效率。
  2. 前后端耦合度太高,使用前后端分离方式进行开发,使得前端只需要编写客户端代码,后端只需要编写服务端代码,提供数据接口即可,前端通过ajax请求访问后端数据接口,将model展示到客户端即可
  3. 解决方式,前后端分离,前后端开发者只需要约定好接口文档(URL,参数,参数类型),独立开发即可,前端可以造假数据进行测试,完全不需要后端,两个独立开发,最终对接即可,真正实现了前后端应用的解耦合,极大的提升了开发效率。
  4. 单体应用—>纯前端应用和后端应用,前端应用:数据展示和用户交互,后端应用:负责处理数据接口,前端的html通过ajax,基于restful的后端接口

传统单体应用

在这里插入图片描述

基于前后端分离的方式

在这里插入图片描述

总结:请后端分离就是将一个单体应用拆分成两个独立的应用。前端应用于后端应用通过json格式的数据进行交互

技术栈:springboot+vue

springboot:后端应用开发

vue:前端应用开发

Element ui 后台管理系统主要标签

  1. el-container:构建整个框架

  2. el-aside:构建左侧菜单

  3. el-menu:构建左侧菜单内容,常用属性

    ? :default-openeds 默认展开菜单,通过菜单的index值关联

    ? :default-active 默认选中的菜单,通过菜单的index值关联

    4.el-submenu:可展开的菜单。常用属性

    ? index:菜单的下标,文本类型,不能是数值类型

    5.template:对应el-submenu的菜单名

    6.i标签:设置菜单图标,通过class属性设置

    ? el-icon-message

    ? el-icon-menu

    ? el-icon-setting

    7.el-menu-item:菜单的子节点,常用属性

    ? index:菜单的下标,文本类型,不能是数值类型

vue router动态加载右侧菜单

  • 导航一

    • 页面一
    • 页面二
  • 导航二

    • 页面三
    • 页面四

menu与router的绑定

  1. 标签添加router属性
  2. 在页面中添加标签,来动态渲染router
  3. 标签里的index值就是要跳转的router

Elementui表单校验

定义rules对象,在rules对象中设置表单各个选项的校验规则,具体语法如下

<el-form-item label="书名" prop="name">

rules: {
  name: [
    { required: true, message: '请输入书籍名称', trigger: 'blur' },
    { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
  ]
  }
  
  //给rules里面的name选项设置校验规则,name对应<el-form-item>标签的prop属性
  //required: true			是否为必填项
 // message: '请输入书籍名称'			提示消息
 //trigger: 'blur'   触发3事件,失去焦点

项目实战

数据库表

在这里插入图片描述

项目技术栈

  1. 前端界面ui:ElementUI
  2. 前端技术:VUE
  3. 数据库:mysql
  4. 后端技术:springboot
  5. 操作数据库:springboot jpa

前端UI设计

  1. 页面布局:侧边栏+头部+主体
  2. 侧边栏的数据从vue的index.js路由页面读取
 <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
        <el-menu router :default-openeds="['0','1']">
          <el-submenu v-for="(item,index) in $router.options.routes" :key="index" :index="index+''">
            <template slot="title"><i class="el-icon-message"></i>{{item.name}}</template>
            <el-menu-item v-for="(item2,index2) in item.children" :key="index" :index="item2.path" :class="$route.path==item2.path?'is-active':''">{{item2.name}}</el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>



index.js路由配置

routes:[
    {
      path: '/',
      name:'书籍列表',
      component: Container,
      redirect:"/page1",
      children:[
        {
          path: '/page1',
          name:'添加书籍',
          component: page1
        },
        {
            path: '/page2',
            name:'修改用户',
            component: page2
        }
          ]
    },
    {
      path: '/',
      name: '课程管理',
      component: Container,
      children: [
        {
          path: '/page3',
          name: '课程查看',
          component: page3
        },
        {
          path: '/page4',
          name: '课程添加',
          component: page4
        }
      ]
    }
    ]

扩展:若index.js中配置了一些必须要的路由但是侧边栏并不需要这些路由时,可以在路由里添加一个属性,通过v-if来决定是否需要展示

侧边栏具体效果:

在这里插入图片描述

3.展示数据UI

<template>
<div>
  <el-table
    :data="tableData"
    border
    style="width: 100%">

    <el-table-column
      fixed
      prop="id"
      label="编号"
      width="150">
    </el-table-column>

    <el-table-column
      prop="name"
      label="书名"
      width="120">
    </el-table-column>

    <el-table-column
      prop="author"
      label="作者"
      width="120">
    </el-table-column>

    <el-table-column
      label="操作"
      width="100">
      <template slot-scope="scope">
        <el-button @click="edit(scope.row)" type="text" size="small">修改</el-button>
        <el-button @click="deleteBook(scope.row)" type="text" size="small">删除</el-button>
      </template>
    </el-table-column>

  </el-table>

  <el-pagination
    background
    layout="prev, pager, next"
    page-size="5"
    :total="total"
    @current-change="page">
  </el-pagination>
</div>
</template>

<script>
  export default {
    methods: {
      edit(row) {
        this.$router.push({
          path:'/page3',
          query:{
            id:row.id
          }
        })
      },
      deleteBook(row) {
        const _this = this
        this.$confirm('是否确定删除《' + row.name + '》', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          _this.$axios.delete('http://localhost:8181/book/delete/' + row.id).then(function (response) {
            // _this.$router.push('/page1')
            window.location.reload();//动态刷新页面
          }),this.$message({
            type: 'success',
            message: '删除成功!'
          });
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });
        });
      },
      page(currenPage){
        const _this = this
        this.$axios.get("http://localhost:8181/book/findAll/"+currenPage+"/5").then(function (response) {
          _this.tableData = response.data.content
          _this.total = response.data.totalElements
        })
      }
    },
    created() {
      const _this = this
      this.$axios.get("http://localhost:8181/book/findAll/1/5").then(function (response) {
        _this.tableData = response.data.content
        _this.total = response.data.totalElements
      })
    },

    data() {
      return {
        total:null,
        tableData: null
      }
    }
  }
</script>

展示数据UI的form表单通过:data=“tableData”,与data()里面的tableData动态绑定了

分页条的total属性与data里面的total动态绑定,同时点击下一页时触发了一个点击事件,从而达到页面切换效果

<el-pagination
    background
    layout="prev, pager, next"
    page-size="5"
    :total="total"
    @current-change="page">
  </el-pagination>

界面展示:

在这里插入图片描述

4.添加页面UI

<template>
  <el-form style="width: 40%" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
    <el-form-item label="书名" prop="name">
      <el-input v-model="ruleForm.name"></el-input>
    </el-form-item>

      <el-form-item label="作者" prop="author">
        <el-input v-model="ruleForm.author"></el-input>
      </el-form-item>

    <el-form-item>
      <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
      <el-button @click="resetForm('ruleForm')">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
  export default {
    data() {
      return {
        ruleForm: {
          name: '',
          author:''
        },
        rules: {
          name: [
            { required: true, message: '请输入书籍名称', trigger: 'blur' },
            { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
          ],
          author: [
            { required: true, message: '请输入书籍作者', trigger: 'change' }
          ]
        }
      };
    },
    methods: {
      submitForm(formName) {
        const _this = this
        this.$refs[formName].validate((valid) => {
          if (valid) {
            _this.$axios.post('http://localhost:8181/book/save',_this.ruleForm).then(function (response) {
              // console.log(response)
              if(response.data=='success'){
                _this.$alert('《'+_this.ruleForm.name+'》添加成功', '消息', {
                  confirmButtonText: '确定',
                  callback: action => {
                    _this.$router.push('/page1')
                  }
                });
                // _this.$router.push('/page1')
                // _this.$message('添加成功')
              }
            })
            // alert('submit!');
            console.log(_this.ruleForm)
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }
  }
</script>

添加页面实现了数据的校验,以及良好的数据提示页面,给用户良好的体验

界面展示:
在这里插入图片描述

5.修改页面UI

<template>
  <el-form style="width: 40%" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
    <el-form-item label="书名" prop="name">
      <el-input v-model="ruleForm.name"></el-input>
    </el-form-item>

    <el-form-item label="作者" prop="author">
      <el-input v-model="ruleForm.author"></el-input>
    </el-form-item>

    <el-form-item>
      <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
      <el-button @click="resetForm('ruleForm')">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
  export default {
    data() {
      return {
        ruleForm: {
          name: '',
          author:''
        },
        rules: {
          name: [
            { required: true, message: '请输入书籍名称', trigger: 'blur' },
            { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
          ],
          author: [
            { required: true, message: '请输入书籍作者', trigger: 'change' }
          ]
        }
      };
    },
    methods: {
      submitForm(formName) {
        const _this = this
        this.$refs[formName].validate((valid) => {
          if (valid) {
            _this.$axios.put('http://localhost:8181/book/update',_this.ruleForm).then(function (response) {
              // console.log(response)
              if(response.data=='success'){
                _this.$alert('《'+_this.ruleForm.name+'》修改成功', '消息', {
                  confirmButtonText: '确定',
                  callback: action => {
                    _this.$router.push('/page1')
                  }
                });
                // _this.$router.push('/page1')
                // _this.$message('添加成功')
              }
            })
            // alert('submit!');
            console.log(_this.ruleForm)
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    },
    created() {
      const _this = this
      this.$axios.get("http://localhost:8181/book/findById/"+this.$route.query.id).then(function (response) {
        console.log(response.data)
        _this.ruleForm = response.data
      })
      // alert(this.$route.query.id)
    }
  }
</script>

<style scoped>

</style>

因为直接从前端获取的数据不安全,容易被篡改,所以数据统一从数据库中查询出来,展示到页面之中,同时修改界面也有良好的用户体验

界面展示:

在这里插入图片描述

6.删除功能:

 <el-button @click="deleteBook(scope.row)" type="text" size="small">删除</el-button>


deleteBook(row) {
        const _this = this
        this.$confirm('是否确定删除《' + row.name + '》', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          _this.$axios.delete('http://localhost:8181/book/delete/' + row.id).then(function (response) {
            // _this.$router.push('/page1')
            window.location.reload();//动态刷新页面
          }),this.$message({
            type: 'success',
            message: '删除成功!'
          });
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });
        });
      }

删除操作给用户足够的反应时间以及误操作的取消操作

界面展示:
在这里插入图片描述

后端接口提供

  1. 解决跨域数据传输问题:
package com.sunset.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CrosConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedHeaders("Content-Type","X-Requested-With","accept,Origin","Access-Control-Request-Method","Access-Control-Request-Headers","token")
                .allowedMethods("*")
                .allowedOriginPatterns("*")
                .allowCredentials(true);
    }
}

2.实体类定义

package com.sunset.entity;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;@Entitypublic class Book {    @Id//主键    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增    private Integer id;    private String name;    private String author;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getAuthor() {        return author;    }    public void setAuthor(String author) {        this.author = author;    }}

3.dao层定义(适用springboot jpa操作数据库)

package com.sunset.repository;import com.sunset.entity.Book;import org.springframework.data.jpa.repository.JpaRepository;public interface BookRepository extends JpaRepository<Book,Integer> {//泛型,第一个代表对应的实体类,第二个泛型代表主键}

4.service层:因为业务较为简单,所以没有写service层

5.controller层

package com.sunset.controller;import com.sunset.entity.Book;import com.sunset.repository.BookRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.data.domain.Pageable;import org.springframework.web.bind.annotation.*;@RestController@RequestMapping("/book")public class BookController {    @Autowired    private BookRepository bookRepository;    @GetMapping("/findAll/{page}/{size}")    public Page<Book> findAll(@PathVariable("page") Integer page, @PathVariable("size") Integer size){        Pageable pageable = PageRequest.of(page-1,size);        return bookRepository.findAll(pageable);    }    @PostMapping("/save")    public String save(@RequestBody Book book){        Book result = bookRepository.save(book);        if(result!=null){            return "success";        }else return "error";    }    @GetMapping("/findById/{id}")    public Book findById(@PathVariable("id") Integer id){        return bookRepository.findById(id).get();    }    @PutMapping("/update")    public String update(@RequestBody Book book){        Book result = bookRepository.save(book);        if(result!=null){            return "success";        }else return "error";    }    @DeleteMapping("/delete/{id}")    public String delete(@PathVariable("id") Integer id){        bookRepository.deleteById(id);        return "success";    }}

controller层统一采用restful风格编程

前后端通信

采用vue集成的axios进行数据传输,与通信

egg:修改书籍信息模块:

  • 用户点击修改按钮,触发事件edit()
 <el-button @click="edit(scope.row)" type="text" size="small">修改</el-button> edit(row) {        this.$router.push({          path:'/page3',          query:{            id:row.id          }        })      }
  • edit事件将路由push到page3页面,同时将id传过去
  • page3页面在路由刚请求的时候加载created函数,该函数与后端的接口进行数据传输,将数据保存在data里面。进行数据的双向绑定
created() {      const _this = this      this.$axios.get("http://localhost:8181/book/findById/"+this.$route.query.id).then(function (response) {        console.log(response.data)        _this.ruleForm = response.data      })      // alert(this.$route.query.id)    }  }
  • 用户根据要求重新修改数据后,点击提交按钮触发submitForm事件
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>

submitForm(formName) {
        const _this = this
        this.$refs[formName].validate((valid) => {
          if (valid) {
            _this.$axios.put('http://localhost:8181/book/update',_this.ruleForm).then(function (response) {
              // console.log(response)
              if(response.data=='success'){
                _this.$alert('《'+_this.ruleForm.name+'》修改成功', '消息', {
                  confirmButtonText: '确定',
                  callback: action => {
                    _this.$router.push('/page1')
                  }
                });
                // _this.$router.push('/page1')
                // _this.$message('添加成功')
              }
            })
            // alert('submit!');
            console.log(_this.ruleForm)
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      }
  • submitForm事件与后端提供的数据接口进行对接,通过axios进行数据通信、

总结

该前后端分离项目相较之下还是比较简单的,但实现了基本的CRUD操作,前端通过VUE,ElementUI进行架构,后端通过springboot,基于restful的数据接口进行对接,实现了前后端的解耦合

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 11:11:21  更:2021-07-16 11:12:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/20 18:40:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码