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
- 为你的IDE装上 vue.js 插件,开发起来会更舒服一些
- 很多内容我也只是知道形式,而不清楚原理,所以如果阅读官方文档有助于加深理解
-- vue.js 中文文档 (https://cn.vuejs.org/)
-- Element-ui vue 中文文档 (http://element-cn.eleme.io/#/zh-CN/component/installation)
-- vue-router 2 中文文档 (https://router.vuejs.org/zh-cn/)
-- vuex github (https://github.com/vuejs/vuex)
脚手架创建
打开命令行,进入到你的工作空间,我们使用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下生成的文件就是我们需要的内容了,目录结构大致是这样的
双击 index.httml ,页面可以被直接打开,这就是编译后的静态文件了。
还记得第一篇文章我们为项目建的临时主页吗?是时候换个更漂亮的了。把dist下所有文件copy,然后粘贴在SpringBoot项目resources/static文件夹下,启动SpringBoot项目,访问
这种属于手工整合方式,对于个人开发还是比较方便的,毕竟最后我们只需要在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": {
//...开发时依赖的工具
}
}
写到这里,我们来改造一下目录结构,改造成自己喜欢的样子
首先看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" >
<!-- 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>
剩下的源码我就不贴在文章中了,登陆实例大量参考 tong2-family
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) } )
项目展示及源码
- 服务端代码 spring-vue-demo
- 前端项目代码 spring-vue-demo