msg: ‘Hello Vue!’
}
}
}
vue-cli会我们的更改进行热更新,再次打开 http://localhost:8080/,界面发生改变:

2.1、后端项目创建
后端项目创建如下:
- 打开Idea,
New Project,选择Spring Intializr

- 填入项目的相关信息

- SpringBoot版本选择了 2.3.8 , 选择了web 和 MySQL驱动依赖

- 创建完成的项目

- 项目完整pom.xml
<project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd”>
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.8.RELEASE
cn.fighter3
demo-java
0.0.1-SNAPSHOT
demo-java
Demo project for Spring Boot
<java.version>1.8</java.version>
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-maven-plugin
2.3、引入MybatisPlus
如果对MybatisPlus不熟悉,入门可以参考 SpringBoot学习笔记(十七:MyBatis-Plus )
想了解更多可以直接查看官网。
2.3.1、引入MP依赖
com.baomidou
mybatis-plus-boot-starter
3.4.1
由于本实例的数据库表非常简单,只有一个单表,所以这里我们直接将基本的增删改查写出来
2.3.2、数据库创建
数据库设计非常简单,只有一张表。

建表语句如下:
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id int(11) NOT NULL AUTO_INCREMENT,
login_name varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT ‘登录名’,
user_name varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT ‘用户名’,
password varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT ‘密码’,
sex varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘性别’,
email varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘邮箱’,
address varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘地址’,
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
2.3.3、配置
在application.properties 中写入相关配置:
服务端口号
server.port=8088
数据库连接配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
在启动类里添加 @MapperScan注解,扫描 Mapper 文件夹:
@SpringBootApplication
@MapperScan(“cn.fighter3.mapper”)
public class DemoJavaApplication {
public static void main(String[] args) {
SpringApplication.run(DemoJavaApplication.class, args);
}
}
2.3.3、相关代码
MP提供了代码生成器的功能,可以按模块生成Controller、Service、Mapper、实体类的代码。在数据库表比较多的情况下,能提升开发效率。官网给出了一个Demo,有兴趣的可以自行查看。
- 实体类
/**
-
@Author: 三分恶
-
@Date: 2021/1/17
-
@Description: 用户实体类
**/
@TableName(value = “user”)
public class User {
@TableId(type = IdType.AUTO)
private Integer id;
private String loginName;
private String userName;
private String password;
private String sex;
private String email;
private String address;
//省略getter、setter等
}
- Mapper接口:继承BaseMapper即可
/**
-
@Author: 三分恶
-
@Date: 2021/1/17
-
@Description: TODO
**/
public interface UserMapper extends BaseMapper {
}
OK,到此单表的增删改查功能已经完成了,是不是很简单。
可以写一个单元测试测一下。
2.3.4、单元测试
@SpringBootTest
class UserMapperTest {
@Autowired
UserMapper userMapper;
@Test
@DisplayName(“插入数据”)
public void testInsert(){
User user=new User(“test1”,“test”,“t123”,“男”,“test1@qq.com”,“满都镇”);
Integer id=userMapper.insert(user);
System.out.printf(id.toString());
}
@Test
@DisplayName(“根据id查找”)
public void testSelectById(){
User user=userMapper.selectById(1);
System.out.println(user.toString());
}
@Test
@DisplayName(“查找所有”)
public void testSelectAll(){
List userList=userMapper.selectObjs(null);
System.out.println(userList.size());
}
@Test
@DisplayName(“更新”)
public void testUpdate(){
User user=new User();
user.setId(1);
user.setAddress(“金葫芦镇”);
Integer id=userMapper.updateById(user);
System.out.println(id);
}
@Test
@DisplayName(“删除”)
public void testDelete(){
userMapper.deleteById(1);
}
}
至此前后端项目基本搭建完成,接下来开始进行功能开发。
====================================================================
1.1、登录界面
在前面访问页面的时候,有一个 V logo,看起来比较奇怪,我们先把它去掉,这个图片的引入是在根组件中——src\App.vue ,把下面一行注释或者去掉。

在src目录下新建文件夹views,在views下新建文件 login.vue
登录
用户名:
密码:
登录
1.2、添加路由
在 router\index.js 里添加路由,代码如下:
import Vue from ‘vue’
import Router from ‘vue-router’
import HelloWorld from ‘@/components/HelloWorld’
//导入登录页面组件
import Login from ‘@/views/login.vue’
Vue.use(Router)
export default new Router({
routes: [
{
path: ‘/’,
name: ‘HelloWorld’,
component: HelloWorld
},
//添加登录页面路由
{
path:‘/login’,
name: ‘Login’,
component: Login
}
]
})
OK,现在在浏览器里输入 http://localhost:8080/#/login ,就可以访问登录页面:

页面有点粗糙简陋对不对,没关系,我们可以引入ElmentUI ,使用ElementUI中已经成型的组件。
1.3、引入ElementUI美化界面
Element 的官方地址为 http://element-cn.eleme.io/#/zh-CN ,官方文档比较好懂,大部分组件复制粘贴即可。

1.3.1、安装Element UI
在vscode 中打开终端,运行命令npm i element-ui -S ,就安装了 element ui 最新版本—当前是 2.15.0

1.3.2、引入 Element
引入分为完整引入和按需引入两种模式,按需引入可以缩小项目的体积,这里我们选择完整引入。
根据文档,我们需要修改 main.js 为如下内容:
// The Vue build version to load with the import command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from ‘vue’
import App from ‘./App’
import router from ‘./router’
//引入ElementUI
import ElementUI from ‘element-ui’
import ‘element-ui/lib/theme-chalk/index.css’
Vue.config.productionTip = false
/* eslint-disable no-new */
Vue.use(ElementUI)
new Vue({
el: ‘#app’,
router,
components: { App },
template: ‘’
})
1.3.3、使用ElementUI美化登录页面
现在开始使用 ElementUI和 css美化我们的登录界面,修改后的login.vue代码如下:
系统登录
<el-input
type=“text”
v-model=“loginForm.loginName”
auto-complete=“off”
placeholder=“账号”
<el-input
type=“password”
v-model=“loginForm.password”
auto-complete=“off”
placeholder=“密码”
<el-button
type=“primary”
style=“width: 100%; border: none”
登录</el-button
需要注意:
-
在
src\assets路径下新建一个一个文件夹img,在 img 里放了一张网上找到的无版权图片作为背景图 -
App.vue里删了一行代码,不然会有空白:
margin-top: 60px;
好了,看看我们修改之后的登录界面效果:

OK,登录界面的面子已经做好了,但是里子还是空的,没法和后台交互。
1.4、引入axios发起请求
相信大家都对 ajax 有所了解,前后端分离情况下,前后端交互的模式是前端发出异步式请求,后端返回 json 。
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。在这里我们只需要知道它是非常强大的网络请求处理库,且得到广泛应用即可。
在项目目录下运行命令 npm install --save axios ,安装模块:

在 main.js 里全局注册 axios:
var axios = require(‘axios’)
// 全局注册,之后可在其他组件中通过 this.$axios 发送数据
Vue.prototype.$axios = axios
那么怎么使用 axios 发起请求呢?
在 login.vue中添加方法:
methods: {
login () {
this.$axios
.post(‘/login’, {
loginName: this.loginForm.loginName,
password: this.loginForm.password
})
.then(successResponse => {
if (successResponse.data.code === 200) {
this.$router.replace({path: ‘/’})
}
})
.catch(failResponse => {
})
}
},
这个方法里通过 axios 向后台发起了请求,如果返回成功的结果就跳转到 / 路由下。
在登录按钮里触发这个方法:
<el-button
type=“primary”
style=“width: 100%; border: none”
@click=“login”
登录</el-button
那么现在就能向后台发起请求了吗?还没完。
1.5、前端相关配置
- 反向代理
修改 src\main.js ,添加反向代理的配置:
// 设置反向代理,前端请求默认发送到 http://localhost:8888/api
axios.defaults.baseURL = ‘http://localhost:8088/api’
这么一来,我们在前面写的登录请求,访问的后台地址实际就是 http://localhost:8088/api/login
- 跨域配置
前后端分离会带来一个问题—跨域,关于跨域,这里就不展开讲解。在 config\index.js 中,找到 proxyTable 位置,修改为以下内容:
proxyTable: {
‘/api’: {
target: ‘http://localhost:8088’,
changeOrigin: true,
pathRewrite: {
‘^/api’: ‘’
}
}
},
2.1、统一结果封装
这里我们创建了一个 Result 类,用于异步统一返回的结果封装。一般来说,结果里面有几个要素必要的
-
是否成功,可用 code 表示(如 200 表示成功,400 表示异常)
-
结果消息
-
结果数据
/**
-
@Author: 三分恶
-
@Date: 2021/1/17
-
@Description: 统一结果封装
**/
public class Result {
//相应码
private Integer code;
//信息
private String message;
//返回数据
private Object data;
//省略getter、setter、构造方法
}
实际上由于响应码是固定的,code 属性应该是一个枚举值,这里作了一些简化。
2.2、登录业务实体类
为了接收前端登录的数据,我们这里创建了一个登录用的业务实体类:
public class LoginDTO {
private String loginName;
private String password;
//省略getter、setter
}
2.3、控制层
LoginController,进行业务响应:
/**
-
@Author: 三分恶
-
@Date: 2021/1/17
-
@Description: TODO
**/
@RestController
public class LoginController {
@Autowired
LoginService loginService;
@PostMapping(value = “/api/login”)
@CrossOrigin //后端跨域
public Result login(@RequestBody LoginDTO loginDTO){
return loginService.login(loginDTO);
}
}
2.4、业务层
业务层进行实际的业务处理。
- LoginService:
public interface LoginService {
public Result login(LoginDTO loginDTO);
}
- LoginServiceImpl:
/**
-
@Author: 三分恶
-
@Date: 2021/1/17
-
@Description:
**/
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private UserMapper userMapper;
@Override
public Result login(LoginDTO loginDTO) {
if (StringUtils.isEmpty(loginDTO.getLoginName())){
return new Result(400,“账号不能为空”,“”);
}
if (StringUtils.isEmpty(loginDTO.getPassword())){
return new Result(400,“密码不能为空”,“”);
}
//通过登录名查询用户
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq(“login_name”, loginDTO.getLoginName());
User uer=userMapper.selectOne(wrapper);
//比较密码
if (uer!=null&&uer.getPassword().equals(loginDTO.getPassword())){
return new Result(200,“”,uer);
}
return new Result(400,“登录失败”,“”);
}
}
启动后端项目:

访问登录界面,效果如下:

这样一个简答的登录就完成了,接下来,我们会对这个登录进一步完善。
====================================================================
前面虽然实现了登录,但只是一个简单的登录跳转,实际上并不能对用户的登录状态进行判别,接下来我们进一步完善登录功能。
首先开始后端的开发。
1.1、拦截器
在前后端分离的情况下,比较流行的认证方案是 JWT认证 认证,和传统的session认证不同,jwt是一种无状态的认证方法,也就是服务端不再保存任何认证信息。出于篇幅考虑,我们这里不再引入 JWT ,只是简单地判断一下前端的请求头里是否存有 token 。对JWT 认证感兴趣的可以查看文章:SpringBoot学习笔记(十三:JWT ) 。
- 创建
interceptor包,包下新建拦截器LoginInterceptor
/**
-
@Author: 三分恶
-
@Date: 2021/1/18
-
@Description: 用户登录拦截器
**/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
//从header中获取token
String token = request.getHeader(“token”);
//如果token为空
if (StringUtils.isBlank(token)) {
setReturn(response,401,“用户未登录,请先登录”);
return false;
}
//在实际使用中还会:
// 1、校验token是否能够解密出用户信息来获取访问者
// 2、token是否已经过期
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
}
//返回json格式错误信息
private static void setReturn(HttpServletResponse response, Integer code, String msg) throws IOException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader(“Access-Control-Allow-Credentials”, “true”);
httpResponse.setHeader(“Access-Control-Allow-Origin”, HttpContextUtil.getOrigin());
//UTF-8编码
httpResponse.setCharacterEncoding(“UTF-8”);
response.setContentType(“application/json;charset=utf-8”);
Result result = new Result(code,msg,“”);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
httpResponse.getWriter().print(json);
}
}
- 为了能给前端返回 json 格式的结果,这里还用到了一个工具类,新建
util包,util 包下新建工具类HttpContextUtil
/**
-
@Author: 三分恶
-
@Date: 2021/1/18
-
@Description: http上下文
**/
public class HttpContextUtil {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getDomain() {
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin() {
HttpServletRequest request = getHttpServletRequest();
return request.getHeader(“Origin”);
}
}
1.2、拦截器配置
拦截器创建完成之后,还需要进行配置。
/**
-
@Author: 三分恶
-
@Date: 2021/1/18
-
@Description: web配置
**/
@Configuration
public class DemoWebConfig implements WebMvcConfigurer {
/**
-
拦截器配置
-
@param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
registry.addInterceptor(new LoginInterceptor()).addPathPatterns(“/api/**”)
//放行路径,可以添加多个
.excludePathPatterns(“/api/login”);
}
}
1.3、跨域配置
细致的同学可能会发现,在之前的后台接口,有一个注解@CrossOrigin ,这个注解是用来跨域的,每个接口都写一遍肯定是不太方便的,这里我们 创建跨域配置类并添加统一的跨域配置:
/**
-
@Author 三分恶
-
@Date 2021/1/25
-
@Description 跨域配置
*/
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//允许源,这里允许所有源访问,实际应用会加以限制
corsConfiguration.addAllowedOrigin(“*”);
//允许所有请求头
corsConfiguration.addAllowedHeader(“*”);
//允许所有方法
corsConfiguration.addAllowedMethod(“*”);
source.registerCorsConfiguration(“/**”, corsConfiguration);
return new CorsFilter(source);
}
}
1.3、登录service
这样一来,后端就需要生成一个 token 返回给前端,所以更改 LoginServiceImpl 里的登录方法。
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private UserMapper userMapper;
@Override
public Result login(LoginDTO loginDTO) {
if (StringUtils.isEmpty(loginDTO.getLoginName())){
return new Result(400,“账号不能为空”,“”);
}
if (StringUtils.isEmpty(loginDTO.getPassword())){
return new Result(400,“密码不能为空”,“”);
}
//通过登录名查询用户
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq(“login_name”, loginDTO.getLoginName());
User uer=userMapper.selectOne(wrapper);
//比较密码
if (uer!=null&&uer.getPassword().equals(loginDTO.getPassword())){
LoginVO loginVO=new LoginVO();
loginVO.setId(uer.getId());
//这里token直接用一个uuid
//使用jwt的情况下,会生成一个jwt token,jwt token里会包含用户的信息
loginVO.setToken(UUID.randomUUID().toString());
loginVO.setUser(uer);
return new Result(200,“”,loginVO);
}
return new Result(401,“登录失败”,“”);
}
}
其中对返回的data 封装了一个VO:
/**
-
@Author: 三分恶
-
@Date: 2021/1/18
-
@Description: 登录VO
**/
public class LoginVO implements Serializable {
private Integer id;
private String token;
private User user;
//省略getter、setter
}
最后,测试一下登录接口:

OK,没有问题。
前面我们使用了后端拦截器,接下来我们尝试用前端实现相似的功能。
实现前端登录器,需要在前端判断用户的登录状态。我们可以像之前那样在组件的 data 中设置一个状态标志,但登录状态应该被视为一个全局属性,而不应该只写在某一组件中。所以我们需要引入一个新的工具——Vuex,它是专门为 Vue 开发的状态管理方案,我们可以把需要在各个组件中传递使用的变量、方法定义在这里。
2.1引入Vuex
首先在终端里使用命令 npm install vuex --save 来安装 Vuex 。
在 src 目录下新建一个文件夹 store,并在该目录下新建 index.js 文件,在该文件中引入 vue 和 vuex,代码如下:
import Vue from ‘vue’
import Vuex from ‘vuex’
Vue.use(Vuex)
接下来,在index.js 里设置我们需要的状态变量和方法。为了实现登录拦截器,我们需要一个记录token的变量量。同时为了全局使用用户信息,我们还需要一个记录用户信息的变量。还需要改变变量值的mutations。完整的代码如下:
import Vue from ‘vue’
import Vuex from ‘vuex’
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: sessionStorage.getItem(“token”),
user: JSON.parse(sessionStorage.getItem(“user”))
},
mutations: {
// set
SET_TOKENN: (state, token) => {
state.token = token
sessionStorage.setItem(“token”, token)
},
SET_USER: (state, user) => {
state.user = user
sessionStorage.setItem(“user”, JSON.stringify(user))
},
REMOVE_INFO : (state) => {
state.token = ‘’
state.user = {}
sessionStorage.setItem(“token”, ‘’)
sessionStorage.setItem(“user”, JSON.stringify(‘’))
}
},
getters: {
},
actions: {
},
modules: {
}
})
这里我们还用到了 sessionStorage,使用sessionStorage ,关掉浏览器的时候会被清除掉,和 localStorage 相比,比较利于保证实时性。
2.2、修改路由配置
为了能够区分哪些路由需要被拦截,我们在路由里添上一个元数据requireAuth来做是否需要拦截的判断:
{
path: ‘/’,
name: ‘HelloWorld’,
component: HelloWorld,
meta: {
requireAuth: true
}
},
完整的 src\router\index.js 代码如下:
import Vue from ‘vue’
import Router from ‘vue-router’
import HelloWorld from ‘@/components/HelloWorld’
//导入登录页面组件
import Login from ‘@/views/login.vue’
Vue.use(Router)
export default new Router({
routes: [
{
path: ‘/’,
name: ‘HelloWorld’,
component: HelloWorld,
meta: {
requireAuth: true
}
},
//添加登录页面路由
{
path:‘/login’,
name: ‘Login’,
component: Login
}
]
})
2.3、使用钩子函数判断是否拦截
上面我们添加了 requireAuth , 接下来就要用到它了。
钩子函数及在某些时机会被调用的函数。这里我们使用 router.beforeEach(),意思是在访问每一个路由前调用。
打开 src\main.js ,首先添加对 store 的引用
import store from ‘./store’
并修改vue对象里的内容,使 store 能全局使用:
new Vue({
el: ‘#app’,
router,
// 注意这里
store,
components: { App },
template: ‘’
})
解下来,我们写beforeEach() 函数,逻辑很简单,判断是否需要登录,如果是,判断 store中是否存有token ,是则放行,否则跳转到登录页。
//钩子函数,访问路由前调用
router.beforeEach((to, from, next) => {
//路由需要认证
if (to.meta.requireAuth) {
//判断store里是否有token
if (store.state.token) {
next()
} else {
next({
path: ‘login’,
query: { redirect: to.fullPath }
})
}
} else {
next()
}
}
)
完整的 main.js 代码如下:
// The Vue build version to load with the import command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from ‘vue’
import App from ‘./App’
import router from ‘./router’
//引入ElementUI
import ElementUI from ‘element-ui’
import ‘element-ui/lib/theme-chalk/index.css’
import store from ‘./store’
var axios = require(‘axios’)
// 全局注册,之后可在其他组件中通过 this.$axios 发送数据
Vue.prototype.$axios = axios
// 设置反向代理,前端请求默认发送到 http://localhost:8888/api
axios.defaults.baseURL = ‘http://localhost:8088/api’
Vue.config.productionTip = false
/* eslint-disable no-new */
Vue.use(ElementUI)
//钩子函数,访问路由前调用
router.beforeEach((to, from, next) => {
//路由需要认证
if (to.meta.requireAuth) {
//判断store里是否有token
if (store.state.token) {
next()
} else {
next({
path: ‘login’,
query: { redirect: to.fullPath }
})
}
} else {
next()
}
}
)
new Vue({
el: ‘#app’,
router,
// 注意这里
store,
components: { App },
template: ‘’
})
2.4、请求封装
我们前面写的后端拦截器,对请求进行了拦截,要求请求头里携带token,这个怎么处理呢?
答案是封装axios。
在 src 目录下新建目录 utils ,在uitls 目录下新建文件 request.js 。
首先导入 axios 和 store:
import axios from ‘axios’
import store from ‘@/store’
接下来在请求拦截器中,给请求头添加 token :
// request 请求拦截
service.interceptors.request.use(
config => {
if (store.state.token) {
config.headers[‘token’] = window.sessionStorage.getItem(“token”)
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
完整的request.js:
import axios from ‘axios’
import store from ‘@/store’
//const baseURL=“localhost:8088/api”
//创建axios实例
const service = axios.create({
baseURL: process.env.BASE_API, // api的base_url
})
// request 请求拦截
service.interceptors.request.use(
config => {
if (store.getters.getToken) {
config.headers[‘token’] = window.sessionStorage.getItem(“token”)
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
//response响应拦截
axios.interceptors.response.use(response => {
let res = response.data;
console.log(res)
if (res.code === 200) {
return response
} else {
return Promise.reject(response.data.msg)
}
},
error => {
console.log(error)
if (error.response.data) {
error.message = error.response.data.msg
}
if (error.response.status === 401) {
router.push(“/login”)
}
return Promise.reject(error)
}
)
export default service
注意创建axios实例里用到了 baseUrl ,在 config\dev.env.js 里修改配置:
module.exports = merge(prodEnv, {
NODE_ENV: ‘“development”’,
BASE_API: ‘“http://localhost:8088/api”’,
})
这样一封装,我们就不用每个请求都手动来塞 token,或者来做一些统一的异常处理,一劳永逸。 而且我们的 api 可以根据 env 环境变量动态切换。
2.5、封装api
request.js 既然已经封装了,那么接下来就要开始用它。
我们可以像上面的 axios 添加到 main.js 中,这样就能被全局调用。但是有更好的用法。
一般项目中,viess 下放的是我们各个业务模块的视图,对应这些业务模块,我们创建对应的 api 来封装对后台的请求,这样即使业务模块很多,但关系仍然是比较清晰的。
在 src 下新建 api 文件夹,在 api 文件夹下新建 user.js,在user.js 中我们封装了登录的后台请求:
import request from ‘@/utils/request’
export function userLogin(data) {
return request({
url: ‘/login’,
method: ‘post’,
data
})
}
当然,事实上登录用 request.js 不合适,因为request.js 拦截了token,但登录就是为了获取token——所以😅凑合着看吧,谁叫现在就这一个接口呢。
2.6、login.vue
之前的登录组件中,我们只是判断后端返回的状态码,如果是 200,就重定向到首页。在经过前面的配置后,我们需要修改一下登录逻辑,以最终实现登录拦截。
修改后的逻辑如下:
1.点击登录按钮,向后端发送数据
2.受到后端返回的成功代码时,触发 store 中的 mutation ,存储token 和user,
3.获取登录前页面的路径并跳转,如果该路径不存在,则跳转到首页
修改后的 login() 方法如下:
login() {
var _this = this;
userLogin({
loginName: this.loginForm.loginName,
password: this.loginForm.password,
}).then((resp) => {
let code=resp.data.code;
if(code===200){
let data=resp.data.data;
let token=data.token;
let user=data.user;
//存储token
_this.$store.commit(‘SET_TOKENN’, token);
//存储user,优雅一点的做法是token和user分开获取
_this.$store.commit(‘SET_USER’, user);
console.log(_this.$store.state.token);
var path = this.$route.query.redirect
this.$router.replace({path: path === ‘/’ || path === undefined ? ‘/’ : path})
}
});
完整的login.vue:
系统登录
<el-input
type=“text”
v-model=“loginForm.loginName”
auto-complete=“off”
placeholder=“账号”
<el-input
type=“password”
v-model=“loginForm.password”
auto-complete=“off”
placeholder=“密码”
<el-button
type=“primary”
style=“width: 100%; border: none”
@click=“login”
登录</el-button
2.7、HelloWorld.vue
大家应该还记得,到目前为止,我们 的 / 路径还是指向 HelloWorld.vue 这个组件,为了演示 vuex 状态的全局使用,我们做一些更改,添加一个生命周期的钩子函数,来获取 store中存储的用户名:
computed: {
userName() {
return this.$store.state.user.userName
}
}
完整的 HelloWorld.vue:
{{userName}}
2.7、HelloWorld.vue
大家应该还记得,到目前为止,我们 的 / 路径还是指向 HelloWorld.vue 这个组件,为了演示 vuex 状态的全局使用,我们做一些更改,添加一个生命周期的钩子函数,来获取 store中存储的用户名:
computed: {
userName() {
return this.$store.state.user.userName
}
}
完整的 HelloWorld.vue:
{{userName}}
本文介绍了如何使用SpringBoot结合Vue.js实现登录和用户管理功能,从后端项目创建、MybatisPlus的引入、数据库设计到前端登录界面的搭建、ElementUI的使用、axios的引入以及Vuex的状态管理,详细阐述了整个流程。通过这个实例,可以帮助开发者理解前后端分离中的关键技术和面试中可能遇到的问题。

被折叠的 条评论
为什么被折叠?



