20210220-javaee-CURD案例-vue+springboot前后端分离开发过程

vue+springboot前后端分离开发过程

1 技术方案

  • 前后端分离开发
  • 前端使用vue+axios+rap2工具
    • vuecli,用脚手架创建项目
      • Home组件
      • User组件
        • UserAdd子组件
        • UserEdit子组件
      • Student组件
    • 在http://rap2.taobao.org/完成注册,然后创建五个接口模拟
      • /user/findAll接口,返回所有用户
        • GET
        • 入参:{page:1, rows:5}
        • 返回 map:{total:1, totalPage:1, page:1 ,results:[{id:1,name:丰华,age:23,bir:‘1979-12-12’}]}
      • /user/findOne接口,返回一个用户
        • GET
        • 入参:{id:1}
        • 返回:{id:1,name:丰华,age:23,bir:‘1979-12-12’}
      • /user/save接口,增加用户
        • POST
        • 入参:{id:1,name:丰华,age:23,bir:‘1979-12-12’}
        • 返回:map: {success:true, msg: ‘增加用户成功’}
      • /user/edit接口,编辑用户
        • POST
        • 入参:{id:1,name:丰华,age:23,bir:‘1979-12-12’}
        • 返回:map: {success:true, msg: ‘修改用户成功’}
      • /user/delete接口,删除用户
        • GET
        • 入参:{id:1}
        • 返回:map: {success:true, msg: ‘删除用户成功’}
  • 后端使用springboot
    • springboot 2.2.6
    • dependency:
      • starter-web
      • mybatis 2.1.3
      • lombok 1.18.12
      • mysql 5.1.38
      • druid 1.1.19

2 创建前端项目

### 创建前端VUE脚手架项目users
vue init webpack users
cd users
npm start
### 添加axios
Ctrl+C
npm install axios --save-dev
npm start
### 开发完成后,对前端项目进行打包,生成dist目录
Ctrl+C
npm run build

2.1 项目中的文件

/src/App.vue

核心:添加主组件中的导航部分,主要实现导航切换

<template>
  <div id="app">
    <!-- 添加主组件导航 -->
    <a href="#/home">主页</a>
    <a href="#/user">用户管理</a>
    <a href="#/stu">学生管理</a>
    <!-- 路由组件模板 -->
    <router-view/>
  </div>
</template>

/src/main.js

核心:全局导入axios,替换vue中原有的异步请求

import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'    // 导入axios

Vue.config.productionTip = false
Vue.prototype.$http = axios;  // 修改vue内部的$http为axios

/src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from '../components/Home'   // 导入组件
import User from '../components/User'
import UserAdd from '../components/UserAdd'
import UserEdit from '../components/UserEdit'
import Student from '../components/Student'

Vue.use(Router)

export default new Router({
    
    
  routes: [
    {
    
     path: '/', redirect: '/home' },   // 主页路由重定向
    {
    
     path: '/home', name: 'Home', component: Home },   // 主页路由
    {
    
    
      path: '/user', name: 'User', component: User,
      children: [                                       // 组件子路由
        {
    
     path: 'add', name: 'UserAdd', component: UserAdd, },
        {
    
     path: 'edit', name: 'UserEdit', component: UserEdit, },
      ],
    },
    {
    
     path: '/stu', name: 'Student', component: Student },
  ]
})

/src/components/*.vue

即各个组件与子组件开发

  • 数据
  • 方法
  • 钩子函数
  • 子组件

Home.vue

<template>
  <div>
    <h2>欢迎来到我们的主页</h2>
    
  </div>
</template>

User.vue

<template>
  <div>
    <!-- 用户组件模板 -->
    <h2>用户列表</h2>
    <table border="1">
      <tr>
        <td>编号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>生日</td>
        <td>操作</td>
      </tr>
      <tr v-for="emp in emps">
        <td>{
   
   { emp.id }}</td>
        <td>{
   
   { emp.name }}</td>
        <td>{
   
   { emp.age }}</td>
        <td>{
   
   { emp.bir }}</td>
        <td>
          <a href="javascript:;" @click="delRow(emp.id)">删除</a>
          <!-- 这里的href绑定的是前端路由地址,还传了参数,注意单双引号的用法 -->
          <a :href="'#/user/edit?id='+emp.id">编辑</a>
        </td>
      </tr>
    </table>
    <!-- 添加用户 -->
    <a href="#/user/add">添加</a>
    <!-- 子组件模板 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  /* 组件名 */
  name: "User",
  /* 组件数据 */
  data(){
    return {
      emps: [],
    } // 组件的数据必须返回一个对象
  },
  /* 组件方法 */
  methods:{
    findAll(){
      this.$http.get("http://localhost:8989/vue/user/findAll").then((res)=>{
            // console.log(res.data);
            this.emps = res.data.results;
          });
    },
    delRow(id){
      // console.log(id);
      this.$http.get("http://localhost:8989/vue/user/delete?id="+id).then((res)=>{
        if(res.data.success){
          // console.log(res.data);
          alert('删除用户成功,id号是:'+id+',点击确定继续');
          this.findAll();
        }
      });
    },
  },
  /* 子组件 */
  components:{},
  /* 创建时钩子函数,直接触发 */
  created(){
    this.findAll();
  },
  /* 监听,当路由发生变化的时候执行 */
  watch: {
    $route: {
      handler: function(val, oldVal){
        // console.log(val);
        if(val.path=='/user'){
          this.findAll();
        }
      },
      // 深度观察监听
      deep: true
    }
  },
}
</script>

<style>
table {
  margin: 0 auto;
}
</style>

UserAdd.vue

<template>
  <div>
    <!-- 子组件模板 -->
    <h2>添加用户</h2>
    <form action="">
      用户名:<input type="text" v-model="emp.name" > <br>
      用户年龄:<input type="text" v-model="emp.age" > <br>
      用户生日:<input type="text" v-model="emp.bir" > <br>
      <input type="button" @click="saveEmpInfo" value='添加用户'> <br>
    </form>

  </div>
</template>

<script>
export default {
  name: "UserAdd",
  data(){
    return {
      emp: {},
      // emps: [],
    } // 组件的数据必须返回一个对象
  },
  methods:{
    saveEmpInfo(){
      // console.log(this.emp);
      // 带上数据,post提交数据到相应的url
      this.$http.post("http://localhost:8989/vue/user/add",this.emp).then((res)=>{
        console.log(res.data);
        if(res.data.success){
          // 添加成功后,就可以展示用户
          // 切换路由
          this.$router.push("/user");
        }
      });
    }
  },
  components:{},
  created(){

  },
}
</script>

<style>
form {
  margin: 0 auto;
}
</style>

UserEdit.vue

<template>
  <div>
    <!-- 子组件模板 -->
    <h2>编辑用户</h2>
    <form action="">
      用户名:<input type="text" v-model="emp.name" > <br>
      用户年龄:<input type="text" v-model="emp.age" > <br>
      用户生日:<input type="text" v-model="emp.bir" > <br>
      <input type="button" @click="editEmpInfo"  value='编辑用户'> <br>
    </form>

  </div>
</template>

<script>
export default {
  name: "UserEdit",
  data(){
    return {
      emp: {
        id: "", // 编辑过后,需要按id进行更新,必须有id
      },
    } // 组件的数据必须返回一个对象
  },
  methods:{
    findOne(){
      this.$http.get("http://localhost:8989/vue/user/findOne?id="+this.emp.id).then((res)=>{
        console.log(res.data);
        // 按id获取整个员工对象信息,重新渲染数据
        this.emp = res.data;
      });
    },
    editEmpInfo(){
      // 编辑过后,重新提交数据
      this.$http.post("http://localhost:8989/vue/user/edit",this.emp).then((res)=>{
        console.log(res.data);
        if(res.data.success){
          // 提交后,进行前端路由跳转,再发请求,回到查看员工
          this.$router.push("/user");
        }
      });
    },
  },
  components:{},
  created(){
    // 路由传参,前端路由传参跳转过来,这时获取相应的参数
    // console.log('修改组件中获取的id: '+this.$route.query.id);
    this.emp.id = this.$route.query.id;
    this.findOne();
  },
}
</script>

<style>
form {
  margin: 0 auto;
}
</style>

注意,在开发过程中,前端先不管后端,只使用rap2下的模拟接口即可完成测试

在后端完成开发后,再替换成真实的后端接口,即做url替换即可

Student.vue

<template>
  <div>
    <h2>学生列表</h2>
  </div>
</template>

2.2 注意事项

  • 每一个template下,一定要给一个根div

  • 子路由的访问方式

    • #/user/add
    • #/user/edit
    • 给了子路由,需要在子组件中添加<router-view>模板进行展示
  • 前端链接形式

    • 普通路由<a href="#/home">主页</a>
    • 子路由<a href="#/user/add">添加</a>
  • 前端路由传参<a :href="'#/user/edit?id='+emp.id">编辑</a>

  • 监听路由变化,重新发起请求

/* 监听,当路由发生变化的时候执行 */
  watch: {
    
    
    $route: {
    
    
      handler: function(val, oldVal){
    
    
        // console.log(val);
        if(val.path=='/user'){
    
    
          this.findAll();
        }
      },
      // 深度观察监听
      deep: true
    }
  },
  • 删除实现,有待做安全删除
    delRow(id){
    
    
      // console.log(id);
      this.$http.get("http://localhost:8989/vue/user/delete?id="+id).then((res)=>{
    
    
        if(res.data.success){
    
    
          // console.log(res.data);
          alert('删除用户成功,id号是:'+id+',点击确定继续');
          this.findAll();
        }
      });
    },
  • 做完添加和更新后,刷新前端路由
    editEmpInfo(){
    
    
      // 编辑过后,重新提交数据
      this.$http.post("http://localhost:8989/vue/user/edit",this.emp).then((res)=>{
    
    
        console.log(res.data);
        if(res.data.success){
    
    
          // 提交后,进行前端路由跳转,再发请求,回到查看员工
          this.$router.push("/user");
        }
      });
    },
  • 接收和利用前端路由传的参数
  created(){
    
    
    // 路由传参,前端路由传参跳转过来,这时获取相应的参数
    // console.log('修改组件中获取的id: '+this.$route.query.id);
    this.emp.id = this.$route.query.id;
    this.findOne();
  },
  • 做前端开发,要养成先查看拿到的数据的习惯
console.log(xxx);// 拿到数据后再使用,减少不必要的异常调试
  • 心里要清楚
    • 请求时的参数是什么
    • 返回的数据是什么结构,json格式要灵活
      • 增删改是返回的map,拿里面的success字段
      • 查询单个就是返回的user的json
      • 查询所有,本质上是一页的所有数据,返回的是一个map,而在这个map中,用户的数据在results中,它是一个数组

3 后端开发

创建maven项目

改造成springboot项目

分层开发

按rap2的接口来实现controller

3.1 开发过程

直接创建maven webapp项目,手动改造pom文件如下

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>javaee2021-day06</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>javaee2021-day06 Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
  </parent>

  <dependencies>
    <!-- 引入web依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 引入mybatis -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.3</version>
    </dependency>
    <!-- 引入lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      <scope>provided</scope>
    </dependency>
    <!-- 引入mysql -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.38</version>
    </dependency>
    <!-- 引入druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.19</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>javaee2021-day06</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

改造项目架构

src/main/java

​ com.zfh.VueUserApplication.java入口类

​ com.zfh.entity.User.java实体类

​ com.zfh.dao.UserDAO.java接口

​ com.zfh.service.UserService.java接口

​ com.zfh.service.UserServiceImpl.java实现类,自动注入并调用UserDAO接口对象

​ com.zfh.controller.UserContrller.java控制器类,自动注入并调用UserService接口对象

src/main/resources/com/zfh/mapper/UserDAOMapper.xml接口实现配置

src/main/resources 资源目录

src/main/resources/static 静态资源目录

src/main/resources/application.properties 全局配置文件

文件内容参考

application.properties

## spring
spring.application.name=vue
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/vue?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=rootcuit

## server
server.port=8989
server.servlet.context-path=/vue

## mybatis
mybatis.mapper-locations=classpath:com/zfh/mapper/*.xml
mybatis.type-aliases-package=com.zfh.entity

## logging
logging.level.root=info
logging.level.com.zfh.dao=debug
logging.level.com.zfh.service=info

3.2 分层开发

入口类

VueApplication.java

package com.zfh;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class VueCliApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(VueCliApplication.class,args);
    }
}

实体类

User

package com.zfh.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {
    
    
    private String id;
    private String name;
    private Integer age;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    private Date bir;
}

DAO接口类

UserDAO

package com.zfh.dao;

import com.zfh.entity.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserDAO {
    
    
    // 增,增加一个实体
    void save(User user);		
    // 改,编辑一个实体
    void update(User user);
    // 删,按id删除一个实体
    void delete(String id);
    // 查,查找全部实体,或按id查找一个实体
    List<User> findAll();
    User findById(String id);
}

使用mybatis配置实现DAO接口

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zfh.dao.UserDAO">

    <insert id="save" parameterType="com.zfh.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values(#{id},#{name},#{age},#{bir})
    </insert>

    <update id="update" parameterType="com.zfh.entity.User">
        update t_user
        set name = #{name},age=#{age},bir=#{bir}
        where id = #{id}
    </update>

    <delete id="delete" parameterType="String">
        delete from t_user where id = #{id}
    </delete>

    <select id="findById" parameterType="String" resultType="com.zfh.entity.User">
        select id,name,age,bir from t_user where id = #{id}
    </select>

    <select id="findAll" resultType="com.zfh.entity.User">
        select id,name,age,bir from t_user
    </select>
</mapper>

业务接口与实现类

UserService

package com.zfh.service;

import com.zfh.entity.User;

import java.util.List;

public interface UserService {
    
    

    // 增
    void save(User user);
    // 改
    void update(User user);
    // 删
    void delete(String id);
    // 查
    List<User> findAll();
    User findById(String id);
}

UserServiceImpl

package com.zfh.service;

import com.zfh.dao.UserDAO;
import com.zfh.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class UserServiceImpl implements UserService {
    
    

    @Autowired
    private UserDAO userDAO;

    @Override
    public void save(User user) {
    
    
        userDAO.save(user);
    }

    @Override
    public void update(User user) {
    
    
        userDAO.update(user);
    }

    @Override
    public void delete(String id) {
    
    
        userDAO.delete(id);
    }

    @Override
    public List<User> findAll() {
    
    
        return userDAO.findAll();
    }

    @Override
    public User findById(String id) {
    
    
        return userDAO.findById(id);
    }
}

控制器类

实现,注意与RAP2接口一致,方便前端调用

package com.zfh.controller;

import com.zfh.entity.User;
import com.zfh.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@CrossOrigin
@RequestMapping("user")
public class UserController {
    
    

    @Autowired
    private UserService userService;

    /**
     * 查找一个用户
     * @param id
     * @return 返回一个用户信息
     */
    @GetMapping("findOne")
    public User findOne(String id){
    
    
        return userService.findById(id);
    }

    /**
     * 添加用户
     * @param user
     * @return
     */
    @PostMapping("add")
    public Map<String, Object> add(@RequestBody  User user){
    
    
        Map<String, Object> map = new HashMap<>();
        try {
    
    
            userService.save(user);
            map.put("success",true);
            map.put("msg","添加用户成功");
        } catch (Exception e){
    
    
            e.printStackTrace();
            map.put("success",false);
            map.put("msg","添加用户异常:"+ e.getMessage());
        }
        return map;
    }

    /**
     * 更新用户
     * @param user
     * @return
     */
    @PostMapping("edit")
    public Map<String, Object> edit(@RequestBody  User user){
    
    
        Map<String, Object> map = new HashMap<>();
        try {
    
    
            userService.update(user);
            map.put("success",true);
            map.put("msg","更新用户成功");
        } catch (Exception e){
    
    
            e.printStackTrace();
            map.put("success",false);
            map.put("msg","更新用户异常:"+ e.getMessage());
        }
        return map;
    }

    /**
     * 删除用户
     * @param id
     * @return
     */
    @GetMapping("delete")
    public Map<String, Object> delete(String id){
    
    
        Map<String, Object> map = new HashMap<>();
        try {
    
    
            userService.delete(id);
            map.put("success",true);
            map.put("msg","删除用户成功");
        } catch (Exception e){
    
    
            e.printStackTrace();
            map.put("success",false);
            map.put("msg","删除用户异常:"+ e.getMessage());
        }
        return map;
    }

    /**
     * 查找用户
     * @param page
     * @param rows
     * @return
     */
    @RequestMapping("findAll")
    public Map<String,Object> findAll(Integer page, Integer rows){
    
    
        Map<String, Object> map = new HashMap<>();
        List<User> results = userService.findAll();
        map.put("total",10);
        map.put("totalPage",1);
        map.put("page",page);
        map.put("results",results);
        return map;
    }
}

3.3 注意事项

  • 对dao使用@Mapper注解
  • 在业务实现层,使用@Service注解,并使用@Transactional注解启动事务
  • 在控制层,使用@RestContrller注解,开启JSON返回,使用@CrossOrigin允许跨域访问,使用"user"的根访问路径
    • 子路径save
      • 传参:User对象,需要使用@RequestBody获取对象
      • 返回:map{“success”:true/false, “msg”:“操作成功or失败”}
    • 子路径edit
      • 传参:User对象,需要使用@RequestBody获取对象
      • 返回:map{“success”:true/false, “msg”:“操作成功or失败”}
    • 子路径delete
      • 传参:id即可
      • 返回:map{“success”:true/false, “msg”:“操作成功or失败”}
    • 子路径findAll
      • 无参
      • 分页参数:page,rows
      • 返回:map{“total”:总记录数, “total”:总页数, “total”:当前页码,“results”:[{UserList}]}
    • 子路径findOne
      • 传参:id即可
      • 返回:一个User对象
  • 日期型格式
  • 在mysql中,日期字段建议选用datetime型,不用timestamp型
  • 使用mybatis时,建议在save时使用useGeneratedKeys="true" keyProperty="id"回传Id值

4 前端开发完成后的发布

npm run build

生成dist目录,制复到springboot项目下的static目录下即可

注意

  • 修改index.html中的文件的引入路径
  • 一定要做足够多的测试

5 后记

  • 前后端分离开发,但人是一个人,即全栈工程师

    • 项目前后端分离
    • 工程题是合体的,综合的,全能的
  • 前后端分离的目的

    • 思路更清晰
    • 方便开发
    • 方便维护
    • 方便扩展
    • 方便使用各种新的框架和技术
  • 与企业级的差距

    • 数据验证未做
    • 前端业务逻辑过于基础,不适合企业级开发
    • 安全性无
    • 系统性无
    • 可用和可靠性无
    • 没有美感,不优雅
    • 后台库过于简略
    • 忽略了javascript,java,database等基础
    • 未考虑高并发和性能


- 返回:map{“success”:true/false, “msg”:“操作成功or失败”}

  • 子路径edit
    • 传参:User对象,需要使用@RequestBody获取对象
    • 返回:map{“success”:true/false, “msg”:“操作成功or失败”}
  • 子路径delete
    • 传参:id即可
    • 返回:map{“success”:true/false, “msg”:“操作成功or失败”}
  • 子路径findAll
    • 无参
    • 分页参数:page,rows
    • 返回:map{“total”:总记录数, “total”:总页数, “total”:当前页码,“results”:[{UserList}]}
  • 子路径findOne
    • 传参:id即可
    • 返回:一个User对象
  • 日期型格式
  • 在mysql中,日期字段建议选用datetime型,不用timestamp型
  • 使用mybatis时,建议在save时使用useGeneratedKeys="true" keyProperty="id"回传Id值

4 前端开发完成后的发布

npm run build

生成dist目录,制复到springboot项目下的static目录下即可

注意

  • 修改index.html中的文件的引入路径
  • 一定要做足够多的测试

5 后记

  • 前后端分离开发,但人是一个人,即全栈工程师

    • 项目前后端分离
    • 工程题是合体的,综合的,全能的
  • 前后端分离的目的

    • 思路更清晰
    • 方便开发
    • 方便维护
    • 方便扩展
    • 方便使用各种新的框架和技术
  • 与企业级的差距

    • 数据验证未做
    • 前端业务逻辑过于基础,不适合企业级开发
    • 安全性无
    • 系统性无
    • 可用和可靠性无
    • 没有美感,不优雅
    • 后台库过于简略
    • 忽略了javascript,java,database等基础
    • 未考虑高并发和性能

猜你喜欢

转载自blog.csdn.net/matrixbbs/article/details/113882739