基于springboot vue element-ui axios 前后端分离(体验)

记录:269

场景

(1)浏览器访问前端服务Front,展现A页面。

(2)A页面发送异步请求到后端服务Back,请求返回后携带后台的数据跳转到B页面。

版本:Spring Boot 2.5.13;Vue 2.6.14;Element UI 2.15.8;Axios 0.27.2

备忘录1:一件事情,如果能用文字描述清楚,并写下来,这就是我要的效果。

备忘录2:壁垒。

参考地址:

Spring Boot:https://spring.io/projects/spring-boot

Vue.js:https://cn.vuejs.org/v2/guide/

element-ui:https://element.eleme.cn/

Axios:https://www.npmjs.com/package/axios

一、前端工程

1.创建前端工程

基于图形化界面创建Vue项目,命名:hub-example-autumn-web,参考如下方式。

地址:https://blog.csdn.net/zhangbeizhen18/article/details/123605192

安装完成后,本例在初始化工程上进行添加相应配置。

2.package.json运行依赖

运行依赖在hub-example-autumn-web/package.json中

"dependencies": {
  "axios": "^0.27.2",
  "core-js": "^3.8.3",
  "element-ui": "^2.15.8",
  "vue": "^2.6.14",
  "vue-router": "^3.5.1",
  "vuex": "^3.6.2"
 }

安装组件格式,例如安装axios。

npm install axios  -S

3.vue.config.js跨域代理配置

在/hub-example-autumn-web/vue.config.js配置跨域的代理配置。

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 18080,
    proxy: {
      '/api': {
        target: 'http://localhost:18081',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
})

说明:

port: 18080是前端服务器端口。

/api的作用,前端页面中的/api路径,会被替换为target中的地址(即后端服务器地址)。其中,/api是自定义命名,只要能对应上即可。/api就是前端在调用后端URL地址时,匹配到了/api就把/api替换为http://localhost:18081,这样就组成了一个完整的后端请求地址。

4.工程入口main.js

在hub-example-autumn-web/src/main.js中配置。

(1)main.js是前端程序执行入口和起点。

(2)使用import引入需要使用的核心组件、样式等模块。

(3)使用Vue.prototype把组件挂载到Vue实例,以便全局使用。

(4)使用Vue.use把插件注入Vue实例。

(5)初始化Vue实例。把router、store等传入。把App.vue组件注入,并把App.vue的根节点#app挂载上。注:本例(默认)App.vue是页面起点。

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from 'axios'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
Vue.prototype.$axios = axios
Vue.use(ElementUI, { size: 'mini' })
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

5.页面入口App.vue

在hub-example-autumn-web/src/App.vue中是页面入口。

一个标配.vue文件包括

<template></template>:在这里可以写原生html标签,可以写基于Vue 2.x或者 Vue 3.x的Element UI标签。这些标签作为页面的骨架。

<script></script>:在这里写Vue代码和JS代码。

<style></style>:在这里写CSS等样式代码。

<template>
  <div id="app">
    <router-view/>
  </div>
</template>
<style lang="less">
// 全局的样式
html, body, #app {
  width: 100%;
  height: 100%;
  font-family: 'Helvetica Neue',
  'Hiragino Sans GB',
  'WenQuanYi Micro Hei',
  'Microsoft Yahei',
  sans-serif;
  padding: 0;
  margin: 0;
  font-size: 16px;
}
</style>

其中, <router-view/>是路由标签,在App.vue的<router-view/>中组装Vue的页面片段。换句话说,路由匹配到的.vue组件,都会通过这个标签,成为App.vue的一部分,然后渲染。

6.路由入口/src/router/index.js

在hub-example-autumn-web/src/router/index.js是路由入口。

(1)导入路由相关模块vue-router。

(2)使用Vue.use注入路由组件。

(3)配置路由规则

其中一个路由配置:

{
  path: '/register',
  name: 'Register',
  component: () => import('../views/Register.vue')
}

使用逻辑:比如请求http://192.168.0.107:18080/register发起后,在前端服务器收到请求后,会去匹配路由中已经定义的规则/register,匹配到后会加载component对应的组件。

(4)初始化路由

/router/index.js如下:

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
  {
    path: '/register',
    name: 'Register',
    component: () => import('../views/Register.vue')
  },
  {
    path: '/index',
    name: 'Index',
    component: () => import('../views/Index')
  }
]
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
export default router

7.Vuex入口/src/store/index.js

本例使用初始化配置。

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

8.封装axios

根据个人习惯对axios进行管理。

(1)创建axios实例、设置请求拦截器、设置应答拦截器

在/hub-example-autumn-web/src/api/axios-request.js中

import axios from 'axios'
const baseURL = '/api'
export function request (config) {
  // 创建axios的实例对象AxiosInstance
  const instance = axios.create({
    baseURL,
    timeout: 3600,
    withCredentials: true,
    headers: {
      'Content-Type': 'application/json; charset=utf-8'
    }
  })
  // 设置axios请求拦截器
  instance.interceptors.request.use(config => {
    return config
  }, err => {
    console.log(err)
  })
  // 设置axios应答拦截器
  instance.interceptors.response.use(res => {
    if (res.status === 200) {
      return res
    } else {
      console.log('axios的response的200还没返回.' + res)
    }
  }, error => {
    console.log(error)
    return Promise.reject(error)
  })
  return instance(config)
}

其中,const baseURL = '/api'和vue.config.js中的/api是对应上的。

withCredentials: true,表示跨域请求是否提供凭据信息。在前端设置后,后端也要设置。

withCredentials是XMLHttpRequest的一个属性,axios封装了XMLHttpRequest。

(2)封装API

把所有调用http的API统一集中写在约定文件中,并以方法的方式暴露。

在/hub-example-autumn-web/src/api/comom-api.js

import { request } from '@/api/axios-request'
const baseApi = '/autumn'
export function register (inputPara) {
  return request({
    url: `${baseApi}/autu/register`,
    method: 'post',
    data: inputPara,
    headers: {
      'Content-Type': 'application/json; charset=utf-8'
    }
  })
}

register函数调用的后端发布的供客户端调用的http方法:

完整URL:http://127.0.0.1:18081/autumn/autu/register

方法:POST

(3)封装API注意

针对get请求和post请求,AxiosRequestConfig内部接收参数不一样。

封装post请求: {method: 'post',data: inputPara}。

封装get请求: {method: get,params: inputPara}。

9.页面

开发页面时,使用基于 Vue 2.x 的Element UI,需要哪种组件,从Element UI官网找到相应组件,把相应的代码拷贝到.vue文件中,再根据实际需求进行改造。

(1)Register.vue

在hub-example-autumn-web/src/views/Register.vue

本例就是从官网找到<el-row></el-row>,<el-form-item><el-form-item>等需要的标签进行改造。

<template>
  <div>
    <el-row type="flex" class="row-bg row-position" align="middle" justify="center">
      <el-col :xl="6" :lg="7">
        <h3>注册信息</h3>
        <el-form :model="registerForm" ref="registerForm" label-position="right" label-width="80px"
                 size="medium">
          <el-form-item label="用户名" prop="username" style="width: 380px; ">
            <el-input v-model="registerForm.userName"></el-input>
          </el-form-item>
          <el-form-item label="密码" prop="password" style="width: 380px; ">
            <el-input type="password" v-model="registerForm.password"></el-input>
          </el-form-item>
          <el-form-item label="城市" prop="city" style="width: 380px;">
            <el-input v-model="registerForm.city" auto-complete="off"></el-input>
          </el-form-item>
          <el-form-item style="width: 380px;">
            <el-button type="primary" @click="submitForm('registerForm')"
                       style="width: 100%;border: none">注册
            </el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
  </div>
</template>
<script>
import { register } from '../api/comom-api'
export default {
  name: 'Register',
  data () {
    return {
      registerForm: {
        userName: 'zhangbeizhen18',
        password: '123456',
        city: '杭州'
      }
    }
  },
  methods: {
    submitForm (formName) {
      this.$refs[formName].validate((valid) => {
        const inputData = JSON.stringify(this.registerForm)
        if (valid) {
          register(inputData).then(res => {
            // 携带参数跳转到Index.vue
            this.$router.push({
              name: 'Index',
              params: { dataInfo: res.data.data }
            })
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }
  }
}
</script>
<style scoped lang="less">
.el-row {
  background-color: #fffdfb;
  height: 100%;
  display: flex;
  align-items: center;
  text-align: center;
  justify-content: center;
}
.row-position {
  padding-top: 10%
}
</style>

(2)Index.vue

在hub-example-autumn-web/src/views/Index.vue展示一条从Register.vue传递的数据。

<template>
  <div>
    <p class="location-msg-p">{{ this.msg }}</p>
  </div>
</template>
<script>
export default {
  name: 'index',
  data () {
    return {
      msg: ''
    }
  },
  mounted () {
    const temp = this.$route.params.dataInfo
    this.msg = temp.welcome + '你的UUID是' + temp.uuid + ',注册成功.'
  }
}
</script>
<style scoped="less">
.location-msg-p {
  position: center;
  text-align: center;
  font-size: 120%
}
</style>

(3)使用this.$router.push传递数据

在Register.vue中发送

this.$router.push({
  name: 'Index',
  params: { dataInfo: res.data.data }
})

(4)使用this.$route接收数据

在Index.vue中接收

const temp = this.$route.params.dataInfo

注意:发送使用this.$router.push,接收使用this.$route.params。

二、后端工程

1. 创建后端工程

使用IntelliJ IDEA 2021.2.3创建基于Maven工程。

2. pom.xml依赖包

本例使用核心依赖。

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <java.version>1.8</java.version>
    <spring.boot.version>2.5.13 </spring.boot.version>
    <spring.boot.maven.plugin.version>2.5.13 </spring.boot.maven.plugin.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot</artifactId>
      <version>${spring.boot.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>${spring.boot.version}</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
      <version>1.18.22</version>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>5.7.22</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.12.0</version>
    </dependency>
</dependencies>

3. application.yml

本例使用核心依赖

server:
  port: 18081
  servlet:
    context-path: /autumn

4. 服务入口类

入口类

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

5.Controller入口

入口类

@Slf4j
@RestController
@RequestMapping("/autu")
public class AutumnController {
  @PostMapping("/register")
  @ResponseBody
  public ResultObj register(@RequestBody RegisterVO registerVO) {
  log.info("AutumnController->register接收参数,registerVO = " + registerVO);
   if (registerVO == null) {
      ResultObj.fail("系统异常.");
   }
   String info = "您好," + registerVO.getUserName() + ",欢迎来到前后端分离技术世界.";
   Map<String, Object> map = new HashMap<>();
   map.put("welcome", info);
   map.put("uuid", IdUtil.simpleUUID());
   log.info("AutumnController->register返回值,ResultObj = " 
     + ResultObj.success(200, "成功", map));
   return ResultObj.success(200, "成功", map);
  }
}

6.跨域配置入口

跨域配置入口

@Configuration
public class CorsConfig implements WebMvcConfigurer {
  @Override
  public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
     .allowedOriginPatterns("*")
     .allowCredentials(true)
     .allowedMethods("GET", "POST", "DELETE", "PUT")
     .maxAge(3600);
  }
}

7.实体类

(1).RegisterVO接收前端传过来的参数对象

@Data
public class RegisterVO {
  private String userName;
  private String password;
  private String city;
}

(2).ResultObj返回给前端的数据对象

@Data
public class ResultObj implements Serializable {
 private static final long serialVersionUID = -820698071194414678L;
 private int code;
 private String msg;
 private Object data;
 public static ResultObj success(int code, String msg, Object data) {
   ResultObj obj = new ResultObj();
   obj.setCode(code);
   obj.setMsg(msg);
   obj.setData(data);
   return obj;
 }
 public static ResultObj fail(String msg) {
   ResultObj obj = new ResultObj();
   obj.setCode(300);
   obj.setMsg(msg);
   obj.setData("");
   return obj;
 }
}

三、前端和后端联调

1.启动前端服务

前端服务: http://localhost:18080/

2.启动后端服务

后端服务: http://localhost:18081/

3.前端发起请求

请求:http://localhost:18080/register

页面:

4.页面使用axios异步请求到后端服务地址

后端地址:http://localhost:18081/autumn/autu/register

点击注册Button将注册信息提交到后端的/autumn/autu/register,并获取返回值。

5.后端接收请求并返回数据

后端接收请求并返回数据

日志截图:

 6.跳转到Index.vue

前端地址:http://localhost:18080/index

页面:

 四、前端和后端报错解决

1.跨域报错解决

报错信息:

Access to XMLHttpRequest at 'http://localhost:18081/autumn/autu/register' from origin 
'http://192.168.0.107:18080' has been blocked by CORS policy: Response to preflight request 
doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present 
on the requested resource.

解决:

(1)在前端vue.config.js跨域代理配置。本例在(一、前端工程的第3节)

(2)在后端实现 WebMvcConfigurer配置跨域。本例在(二、后端工程的第6节)

2.页面之间数据传递报错

发送页面:

this.$router.push({
   name: 'Index',
   params: { dataInfo: res.data.data }
})

接收页面:

this.$route.params.dataInfo

注意:发送使用this.$router,接收使用this.$route。

五、交互逻辑

1.在浏览器输入地址http://localhost:18080/register,

2.前端服务器接收到请求后,获取到路径/register,就拿着这个路径到定义好的路由规则中匹配,匹配到如下规则,然后就加载出对应组件的页面。

{
  path: '/register',
  name: 'Register',
  component: () => import('../views/Register.vue')
}

3.在页面上操作注册Button,会使用axios异步提交表单数据到后端服务器。

地址:http://localhost:18081/autumn/autu/register

4.后台服务器接收到请求后执行处理逻辑并把数据返回给前端。

5.前端的axios接收到请求返回数据后,携带数据,使用this.$router.push跳转到index.vue

6.在浏览器展现页面和数据。

以上,感谢。

2022年5月14日

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值