一直都想嘗試做前后端分離,我之前一直是學 java 的,所以后端選擇了 spring boot;前端選擇了 vue.js 這個輕量、易上手的框架。網上其實已經有了不少 spring boot 和 vue.js 整合的資料,github 上就有好多 repo,但是每當我指望按圖索驥的時候就會出現各種各樣奇怪的 bug,上 stack overflow 問了也沒人搭理。前前后后研究了差不多三個星期,現在總算是理清楚了。
本文重點介紹我在實踐過程中的基本流程,以及我遇到的一個困擾了我好久的問題,就是如何 cors。
框架版本
- spring boot: 2.0.4.release(jdk 是1.8)
- vue.js: 2.x
基本流程
前端:編寫 vue 組件
首先用 vue-cli 搭好腳手架,我這個 demo 用到的第三方庫有:
- axios:負責 http 請求
- bootstrap-vue:bootstrap 和 vue.js 的整合,方便設計頁面
- vue-router:管理路由
- qs:實現 cors
然后寫一個登錄組件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
<!-- 下面是我直接從 bootstrap-vue 文檔抄下來的模板 --> <template> <div> <b-form @submit = "onsubmit" @reset = "onreset" v- if = "show" > <b-form-group id= "exampleinputgroup1" label= "username:" label- for = "exampleinput1" > <b-form-input id= "exampleinput1" type= "text" v-model= "form.username" required placeholder= "enter username" > </b-form-input> </b-form-group> <b-form-group id= "exampleinputgroup2" label= "password:" label- for = "exampleinput2" > <b-form-input id= "exampleinput2" type= "text" v-model= "form.password" required placeholder= "enter password" > </b-form-input> </b-form-group> <b-form-group id= "examplegroup4" > <b-form-checkbox-group v-model= "form.checked" id= "examplechecks" > <b-form-checkbox value= "me" >check me out</b-form-checkbox> <b-form-checkbox value= "that" >check that out</b-form-checkbox> </b-form-checkbox-group> </b-form-group> <b-button type= "submit" variant= "primary" >submit</b-button> <b-button type= "reset" variant= "danger" >reset</b-button> </b-form> </div> </template> <script> //... </script> |
我現在想實現的就是用戶登錄成功之后導航到另一個組件,所以我就又寫了一個歡迎組件:
1
2
3
4
5
|
<template> <div> <h1>welcome!</h1> </div> </template> |
記得配置路由:
// src/router/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import vue from 'vue' import router from 'vue-router' import login from '@/components/login.vue' import information from '@/components/information.vue' vue.use(router) export default new router({ routes: [ { path: '/' , name: 'login' , component: login }, { path: '/information' , name: 'information' , component: information } ] }) |
后端:提供 restful api
因為只有后端提供了接口,前端才能調用,所以現在要進行后端開發。restful 是現在很流行的 api 設計風格,所以我這里也實踐了一下。下面是 controller 的代碼,完整源碼地址附在文末。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@restcontroller @requestmapping ( "/api" ) public class logincontroller { @requestmapping (path = "/login" , method = requestmethod.post) @responsebody public string login( @requestparam string username, @requestparam string password) { // 簡單處理一下,實際開發中肯定是要用到數據庫的 if (username.equals( "123" ) && password.equals( "123" )) { return "successful" ; } else { return "failed" ; } } } |
后端的 api 現在有了,就差前端調用了。但是沒這么簡單,接下來就要解決我前面提到的問題。
實現 cors
在這個 demo 中前端占用的端口是8080,后端是 8088。這就存在跨域的問題,如果不解決的話后端就沒法接收前端的請求。
我參考了 這個例子 ,通過配置 spring mvc 實現了 cors:
1
2
3
4
5
6
7
8
9
10
11
|
@configuration public class corsconfig implements webmvcconfigurer { @override public void addcorsmappings(corsregistry registry) { registry.addmapping( "/**" ) .allowedorigins(all) .allowedmethods(all) .allowedheaders(all) .allowcredentials( true ); } } |
后端配置好了還不行,前端也要有一些配置,要用 axios 順利地發送請求并保證后端能接收到,需要對請求參數做處理。我參考 這個回答 用 qs 庫對請求參數做了處理:
1
2
3
4
|
qs.stringify({ 'username' : this .form.username, 'password' : this .form.password }) |
現在只需完善前端調用后端 api 的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
// login.vue <script> export default { data () { return { form: { username: '' , password: '' , checked: [] }, show: true } }, methods: { onsubmit (evt) { evt.preventdefault(); // 關鍵就在于要對參數進行處理 axios.post( 'http://localhost:8088/api/login' ,qs.stringify({ 'username' : this .form.username, 'password' : this .form.password })).then((response) => { var status = response.data; if (status === 'successful' ) { this .$router.push( '/information' ); } else { alert(response.data.message); } console.log(response); }). catch ((error) => { console.log(response); }); } } } </script> |
至此,終于實現了前后端的分離,并且保證前后端能夠順利交互。
題外話
讓 controller 能獲取請求參數
controller 可能無法獲取請求參數, 這篇文章 提供了一種解決方案。我這個 demo 中并沒有出現 controller 收不到請求參數的問題,但也把這個問題記錄下來,以后可能遇上也說不準。
axios 方法中的 this
我這個 demo 中還試著用 axios 發 get 請求,然后獲取后端響應的 json 數據。
1
2
3
4
5
6
7
8
9
10
11
12
|
// information.vue <template> <div> <h1>welcome!</h1> <div> <b-button @click = "getinfo()" >get your information</b-button> <h2 v- if = "username !== ''" >your username is: {{ username }}</h2> <h2 v- if = "email !== ''" >your email is: {{ email }}</h2> </div> </div> </template> <script> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import axios from 'axios' export default { data () { return { username: '' , email: '' }; }, methods: { getinfo () { axios.get( 'http://localhost:8088/api/information' ) .then(function(response) { this .username = response.data[ 'username' ]; this .email = response.data[ 'email' ]; console.log(response); }). catch (function(error) { console.log(error); }); } } } </script> |
一開始我是這么寫的,乍一看沒什么問題,但是 javascript 就一直報錯:
typeerror: cannot set property 'username' of undefined
搞了很久都沒有解決,直到看到 這篇文章 ,才明白原來是 this 作用域的問題(javascript 的 this 是真的復雜啊!!!)。改成下面這樣就沒問題了:
1
2
3
4
5
6
7
8
|
axios.get( 'http://localhost:8088/api/information' ) .then((response) => { this .username = response.data[ 'username' ]; this .email = response.data[ 'email' ]; console.log(response); }). catch ((error) => { console.log(error); }); |
后來 stack overflow 上有人說不用箭頭函數也行,只需提前把指向 vue 實例的 this 保存在一個變量就行了:
1
2
3
4
5
6
7
8
9
|
var vue = this ; axios.get( 'http://localhost:8088/api/information' ) .then(function (response) { vue.username = response.data[ 'username' ]; vue.email = response.data[ 'email' ]; console.log(response); }). catch ((error) => { console.log(error); }); |
經實踐,這樣也是可以的。
demo 完整源碼
總結
以上所述是小編給大家介紹的spring boot 與 vue.js 整合流程,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:https://www.jianshu.com/p/4402545763c8