先後端分離 Spring Boot + Vue 開發單頁面應用 我的總結(二)

先後端分離 Spring Boot + Vue 開發單頁面應用 我的總結(二)

2018/01/30 更新

關於跨域:在實際開發過程當中,發現跨域問題並非那麼好解決的 由於Springboot安全控制框架使用了Securtiy,它的身份認證基於 JSESSIONID 而axios框架默認是不發送cookie的,所以須要在axios配置中添加javascript

axios.defaults.withCredentials = true

然而由於跨域策略問題,Springboot後端跨域設置也要修改css

@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)
聲明:我不是專業前端,因此前端代碼可能有些醜,請注重搭建思路,不要在乎代碼風格!html

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

開發環境介紹

  • JDK1.8
  • Node v8.9.3
  • npm v5.5.1
  • 開發工具IDEA(安裝Vue.js插件)
  • 數據庫MySQL 57
  • 版本管理工具 Git

服務端搭建

參見上一篇博文 先後端分離 Spring Boot + Vue 開發單頁面應用 我的總結(一)vue

前端項目搭建

工做準備

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

npm 與 cnpm 基本等價,可是不多狀況下cnpm也許有些bug,因此請斟酌使用。java

  • 安裝vue-cli 這是vue的腳手架,能夠很方便的爲咱們搭建一個vue項目,固然若是把vue裝在全局也不錯
$ npm install vue-cli -g
$ npm install vue -g

腳手架建立

打開命令行,進入到你的工做空間,咱們使用vue腳手架來搭建項目node

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

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

項目集成

vue-cli 爲咱們建立了一個前端工程,咱們能夠在此基礎上進行拓展
大部分開過vue教程的同窗都知道ios

$ npm run build

能夠打包工程,編譯後的文件通常在 項目目錄/dist 下,這些都是能夠發佈的版本
vue-cli 建立的項目默認編譯後是部署在服務器上的,但若是我想直接放進SpringBoot項目裏,就須要一些額外的配置。關於項目開發的配置,通常放置在config下的index.js文件中,好比修改啓動的端口等。 config/index.js 省略部分配置git

'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)
  }
)

項目展現及源碼

之後可能完善,謝謝。

相關文章
相關標籤/搜索