一、前提
1.安装node。检查是否安装:node -v ;
2. 安装脚手架vue-cli:npm install -g @vue/cli ,使用 vue -V 查看是否安装成功;
3.新建项目:vue create 项目名 ,选带state和router文件夹的项目;
4.到指定文件夹,执行npm run serve 运行项目;
5.去除一些vue自动生成的相关内容,如views文件夹下的页面及app.vue里的内容等;
6. 创建一个新的git仓库,方便项目代码管理。
二、项目相关配置和引用
1.安装并在main.js引入element UI 或者其他组件库;
安装:npm i element-ui -S
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
2. 安装axios 并创建一个utils文件夹下的request文件来引入axios,对请求进行封装。
安装:npm i axios
// 基于axios封装,网络请求的函数
import axios from 'axios'
// axios.create()创建一个带配置项的自定义axios函数
// myAxios请求的时候,地址baaseURL+url,然后去请求后台
const myAxios = axios.create({
baseURL: 'http://127.0.0.1:8088'
})
// 导出axios自定义函数
export default myAxios
三、登陆注册页面
1. 开始写页面,首先是登录注册静态页面和校验的实现;
2. 登录注册页面跳转,在router文件夹下写跳转路由,import函数路由懒加载导入组件;
const routes = [
{
path: '/reg',
component: () => import('@/views/register')
// webpack提供import函数来路由懒加载导入组件
// 路由懒加载,就是让页面路由路径切换到reg页面时才加载对应组件的代码
// 好处:让首页加载体积更小,打开更快
},
...
]
3. 登录注册的接口联调,在新建的api文件夹下引入封装的axios文件,编写并导出相关接口;
// 封装的是具体的接口请求方法
// 注意:每个方法只负责请求一个url地址
import request from '@/utils/request'
export const registerAPI = ({ username, password, repassword }) => {
// 是一个Promise对象(内部包含原生ajax请求)
// return 这个Promise对象到逻辑页面,去那边对Promis对象提取结果
return request({
url: '/api/reguser',
method: 'post',
// axios传参params,data
// params的对象和参数会拼接在url?后面给后台(query查询字符串)
// data的则会拼接欸在请求体(body参数)
data: {
username,
password,
repassword
}
})
}
4. 登录接口调用成功会返回一个token值,将token存在vuex内存和本地存储,或者通过第三方插件npm i vuex-persistedstate@3.2.1来实现数据持久化;
import Vue from 'vue'
import Vuex from 'vuex'
// vuex默认保存在内存中,所以刷新所有的值都会回归。初始化(无法做持久化存储)
// npm i vuex-persistedstate@3.2.1 借助这个vuex-persistedstate@3.2.1包实现vuex数据持久化
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: '', // 保存token字符串
},
getters: {},
mutations: {
// 保存token
updateToken (state, val) {
state.token = val
},
},
actions: {},
modules: {},
plugins: [
createPersistedState() // 注入持久化插件
]
})
5. 登录成功后跳转到首页,之后的带权限的页面都是根据token值来判断是否跳转到重定向页面。
methods: {
...mapMutations(['updateToken']),
// 登录按钮->点击事件
async loginFn () {
this.$refs.loginRef.validate(async valid => {
if (!valid) return
// 1. 发起登录的请求
const { data: res } = await loginAPI(this.loginForm)
// 2. 登录失败
if (res.status !== 0) return this.$message.error(res.message)
// 3. 登录成功
this.$message.success(res.message)
// 4. 保存到vuex中
this.updateToken(res.token)
// 登录成功之后,跳转到后台主页
this.$router.push('/')
})
}
}
6. 在router文件夹下全局前置路由守卫,当有token且vuex中没有用户信息时,才请求用户信息,实现换个账号能重新请求数据,设置权限拦截控制,没登陆则跳转登录页面;
const whiteList = ['/login', '/reg'] // 白名单(无需登录就可以访问的路由地址)
// 全局前置路由守卫
router.beforeEach((to, from, next) => {
const token = store.state.token
// 如果不去判断store.state.userInfo.username是否有值,则在路由切换会重复触发
if (token) {
if (!store.state.userInfo.username) {
// 本地有token才去请求用户信息
store.dispatch('getUserInfoActions')
}
next()
} else {
// 未登录
if (whiteList.includes(to.path)) {
// 未登录但是能访问路由地址,则放行(全局前置路由守卫不会再触发了,而是匹配路由表,让组件挂载)
next()
} else {
// next()强制切换到登录路径上
next('/login')
}
}
next()
})
四、首页及相关布局和风格限定(从上往下,从左往右)
1. 新建一个layout文件夹对首页框架进行布局;
2. 首页的点击退出操作:是否退出--> 清空token--> 清空存入vuex的用户信息--> 跳转到登录页面;
methods: {
// 退出登录
logoutFn () {
// 询问用户是否退出登录
this.$confirm('您确认退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// TODO:执行退出登录的操作
// 1. 清空 token
this.$store.commit('updateToken', '')
// 2. 清空 存储在vuex中的个人信息
this.$store.commit('updateUserInfo', '')
// 2. 跳转到登录页面
this.$router.push('/login')
})
.catch(err => err)
}
}
3.侧边栏布局实现及动态请求侧边栏数据显示;
4.在axios封装文件request.js中定义请求拦截器,除登录注册外,所有请求都添加Authorization和token;定义权限控制的响应拦截器,当token失效时清空vuex,被迫回到login页面;
// 定义请求拦截器
// api每次调用request都会先走这个请求拦截器
myAxios.interceptors.request.use(
function (config) {
// config配置对象(要请求后台的参数都在这个对象上)
// 在请求前会触发一次,这个return交给axios源码内,根据配置项发起请求
// 登录注册vuex没有token,且不需要携带token,但是其他页面需要
if (store.state.token) {
// 除登陆注册外,所有请求都添加Authorization和token
config.headers.Authorization = store.state.token
}
return config
},
function (error) {
// 请求发起前有异常时进入
// 返回一个拒绝状态的Promis对象
// 类似catch()里面的return
// return 非Promise对象值,会作为成功的结果,返回给下一个Promise对象
return Promise.reject(error)
}
)
// 定义响应拦截器
myAxios.interceptors.response.use(
function (response) {
// 响应状态码为2xx、3xx时进入,形参response为成功的结果
return response
},
function (error) {
// 响应状态码为4xx、5xx时进入,形参error为失败的结果
// 如果那边用try+catch或者catch函数捕获,可以捕获到我们传递过去的这个error变量的值
if (error.response.status === 401) {
// 本次响应是token过期
// 清除vuex中数据,被动切换到登录页
store.commit('updateToken', '')
store.commit('updateUserInfo', '')
router.push('./login')
Message.error('用户身份已过期,请重新登录')
}
return Promise.reject(error)
}
)