前後端分離 Spring Boot Vue 開發單頁面應用 個人總結(二)

前後端分離 Spring Boot   Vue 開發單頁面應用 個人總結(二)

前後端分離 Spring Boot Vue 開發單頁面應用 個人總結(二)

2018/01/30 更新

關於跨域:在實際開發過程中,發現跨域問題並不是那麼好解決的 因為Springboot安全控制框架使用了Securtiy,它的身份認證基於 JSESSIONID 而axios框架預設是不傳送cookie的,因此需要在axios配置中新增

axios.defaults.withCredentials = true

然而因為跨域策略問題,Springboot後端跨域設定也要修改

@Configuration
public class CorsConfig {
/**
允許任何域名使用
允許任何頭
允許任何方法(post、get等)
*/
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// addAllowedOrigin 不能設定為* 因為與 allowCredential 衝突
corsConfiguration.addAllowedOrigin("http://localhost:9528");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
// allowCredential 需設定為true
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}

前言

前情概述: 上一篇寫了SpringBoot服務端的搭建,整體而言與大部分SpringBoot入門的教程一樣,只是新增了一些自己的理解,畢竟開發的時候總是希望有一個自己用起來順手的模板。這篇總結的是用vue-cli搭建一個前端腳手架,並通過手工整合放進SpringBoot專案中,實際生產環境很可能整合方式不一樣,最後使用vue全家桶搭一個登陸例項(沒有美化UI)
宣告:我不是專業前端,所以前端程式碼可能有些醜,請注重搭建思路,不要在意程式碼風格!

吐槽請忽略 前端博文寫了一天,一直沒有很好的思路,因為整合框架比較多各個內容關聯度又很高,介紹詳細了會變成入門大雜燴,所以決定換個思路,只分享一下整合思路,登陸例項就只放原始碼了,程式碼內都有一些詳細的註釋,ES6的語法很花哨(我是java開發人員,ES6語法真的就很花哨)請慢慢研究。抱歉

開發環境介紹

  • JDK1.8
  • Node v8.9.3
  • npm v5.5.1
  • 開發工具IDEA(安裝Vue.js外掛)
  • 資料庫MySQL 57
  • 版本管理工具 Git

服務端搭建

參見上一篇博文 前後端分離 Spring Boot Vue 開發單頁面應用 個人總結(一)

前端專案搭建

工作準備

  • 請安裝node.js(新版自帶npm包管理工具)
  • 建議安裝cnpm 淘寶映象,安裝依賴的時候會更快一些
# -g 安裝在全域性 registry指定國內下載地址
$ npm install cnpm -g --registry=https://registry.npm.taobao.org

npm 與 cnpm 基本等價,但是很少情況下cnpm也許有些bug,所以請斟酌使用。

  • 安裝vue-cli 這是vue的腳手架,可以很方便的為我們搭建一個vue專案,當然如果把vue裝在全域性也不錯
$ npm install vue-cli -g
$ npm install vue -g

腳手架建立

開啟命令列,進入到你的工作空間,我們使用vue腳手架來搭建專案

# 建立一個基於 webpack 模板的新專案
$ vue init webpack vue-springboot-demo
# 建立過程會要求你的專案起名之類,基本直接回車確認就可以
$ cd vue-springboot-demo
$ cnpm install
$ npm run dev

訪問頁面 localhost:8080 出來頁面了就算成功了,Ctrl C y確認退出

專案整合

vue-cli 為我們建立了一個前端工程,我們可以在此基礎上進行拓展
大部分開過vue教程的同學都知道

$ npm run build

可以打包工程,編譯後的檔案一般在 專案目錄/dist 下,這些都是可以釋出的版本
vue-cli 建立的專案預設編譯後是部署在伺服器上的,但如果我想直接放進SpringBoot專案裡,就需要一些額外的配置。關於專案開發的配置,一般放置在config下的index.js檔案中,比如修改啟動的埠等。
config/index.js 省略部分配置

'use strict'
// ...
module.exports = {
dev: {
//...
// 開發環境啟用的主機
host: 'localhost', // can be overwritten by process.env.HOST
// 開發環境啟用的埠
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
//...
},
build: {
// 將index.htlm 指定生成在dist下
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
// 指定靜態檔案 js/css等與index平級
assetsSubDirectory: './',
// 指定引用地址為相對地址,這樣生成的檔案就可以直接開啟了
// 若指定為 '/' 代表是根目錄地址,屬於可以釋出在單獨伺服器上的
assetsPublicPath: './', 
//...
}
}

修改完配置之後

npm run build

在dist下生成的檔案就是我們需要的內容了,目錄結構大致是這樣的
image
雙擊 index.httml ,頁面可以被直接開啟,這就是編譯後的靜態檔案了。
還記得第一篇文章我們為專案建的臨時主頁嗎?是時候換個更漂亮的了。把dist下所有檔案copy,然後貼上在SpringBoot專案resources/static資料夾下,啟動SpringBoot專案,訪問
image
這種屬於手工整合方式,對於個人開發還是比較方便的,畢竟最後我們只需要在SpringBoot匯出一個jar包就可以部署了,而企業自然有自己的部署方式。
因為我是後端,在學完Vue教程後,一直不懂dist下的檔案怎麼使用,甚至以為要在SpringBoot專案中建立工程,後來終於嘗試出來了,特此整理。

簡單的Demo

前端工程建立之後,要怎麼寫就是自己的事情了,我準備了一個特別醜的Demo,主要記錄一些Vue全家桶整合方式,及前後端ajax通訊配置

使用 IDEA 開啟專案,解放生產力,安裝Vue.js外掛能更進一步解放生產力,記得把專案js模式設定為ES6(會有提示)

簡單介紹一下專案依賴

  • vue 核心依賴
  • vue-router 官方路由管理,用於配合專案打包可以很方便得實現單頁面應用
  • vuex 官方狀態管理,用於元件間通訊
  • babel-polyfill 一個使vuex可以相容IE9等低版本核心瀏覽器的指令碼
  • element-ui 一個UI框架,方便快速開發,非必需
  • axios ajax庫

安裝一下專案依賴

# i 是install的簡寫 --save代表在生產環境中使用
$ cnpm i vuex babel-polyfill element-ui axios --save
# vue-router可能在初始化的時候就已經安裝了
$ cnpm i vue-router --save

最後我們的package.json 大致是這樣子的,完整請在原始碼中看

{
"name": "vue-springboot-demo",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "作者", //.
"private": true,
"scripts": {
//...略
},
"dependencies": {
"axios": "^0.17.1",
"babel-polyfill": "^6.26.0",
"element-ui": "^2.0.8",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
},
"devDependencies": {
//...開發時依賴的工具
}
}

寫到這裡,我們來改造一下目錄結構,改造成自己喜歡的樣子
image

首先看src根目錄下的內容

router 和 store 和 axios 資料夾下分別新建一個 index.js 用於配置路由和狀態管理及ajax框架,可以是空檔案,具體內容後面講解

然後是main.js,它會隨著首頁一起載入,通常是整個網頁的入口程式碼,用它來引入模組是最好的。

import Vue from 'vue'
// 載入App.vue 元件
import App from './App.vue'
// 引入router配置檔案
import router from './router'
// 引入ElementUI,可以使用其元件
import ElementUI from 'element-ui'
// css檔案需手動引入
import 'element-ui/lib/theme-chalk/index.css'
// 引入vuex配置檔案
import store from './store'
// 引入ajax框架axios配置
import axios from './axios'
// 設定 Vue.config.productionTip = false 來關閉生產模式下給出的提示
Vue.config.productionTip = false
// 代表使用ElementUI元件
Vue.use(ElementUI)
// 將axios掛載到Vue原型上方便呼叫
Vue.prototype.$ajxj = axios
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})

App.vue ,.vue字尾名的檔案是Vue提供的單檔案元件方式單檔案元件 核心由template標籤組成,script標籤可以掛載Vue例項等指令碼,形成元件,style標籤可以用來寫css。
因為本人做頁面水平有限,前端Demo做得非常醜,主要是記錄是各個模組的配置,敬請諒解!

<template>
<!-- 這裡的Html寫的比較醜,是(技術有限)為了佈局結構更清晰一些 -->
<div id="app" style="height:600px">
<!-- element的佈局 v-if:根據返回值決定是否渲染  -->
<el-container v-if="isLogin" class="main-container" >
<el-header >
<!-- header元件 -->
<Header></Header>
</el-header>
<el-container>
<!-- 導航元件 -->
<Nav></Nav>
<el-main>
<!-- 這裡放置的是路由的頁面 -->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
<!-- 登陸元件,v-if:根據返回值決定是否渲染 -->
<Login v-if="!isLogin"></Login>
</div>
</template>
<script>
import Nav from './components/nav.vue'
import Header from './components/header.vue'
import Login from './pages/login.vue'
export default {
components: {
// ES6簡寫語法 Nav:Nav
Nav,
Header,
Login
},
name: 'app',
computed: {
isLogin () {
// 讀取全域性狀態,獲取使用者是否登陸,決定渲染狀態
return this.$store.state.login.isLogin
}
}
}
</script>
<style>  
#app {
font-family: Helvetica, sans-serif;
text-align: center;
}  
.main-container {
height: 100%;
border: 1px solid #eee;
margin: 10px 50px 0 50px;
}
/* 加上紅色邊框看出佈局 */
.el-container, .el-aside {
border: 1px solid red;
}
</style>

吐槽,部落格的markdown文字編輯code部分經常會有bug,上面那段我改不過來格式了,請參照原始碼
剩下的原始碼我就不貼在文章中了,登陸例項大量參考 git傳送門 thaks again!
這裡重點講通過axios與SpringBoot伺服器通訊會遇到兩個問題

  • 一是跨域問題
    開發過程中,前後端通常都是啟用單獨的埠,因此會有跨域的問題,在第一篇文章的最後,配置了一個CrosConfig就跨域解決了
  • 二是由於Content-type的問題,會導致controller無法拿到post請求的資料,網路上解決方法很多試了都不可以,最後我嘗試了一個,測試是可以通過的
    axios有攔截器功能,可以方便得為全域性做配置
// 設定content-type
// 這裡處理的是 針對SpringMVC Controller 無法正確獲得請求引數的問題
axios.interceptors.request.use(
config => {
let data = config.data
let key = Object.keys(data)
// 重寫data,由{"name":"name","password":"password"} 改為 name=name&password=password
config.data = encodeURI(key.map(name => `${name}=${data[name]}`).join('&'))
// 設定Content-Type
config.headers = {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
return config
},
error => {
return Promise.reject(error)
}
)

專案展示及原始碼

以後可能完善,謝謝。