文件目录
我们要在src 文件夹操作这些内容。
├─api // 所有请求
├─assets // 主题 字体等静态资源
├─components // 全局公用组件
├─layout // 导航栏
├─router // 路由
├─store // 全局 store管理
├─utils // 全局公用方法
├─views // view
└─login //登录页面
└─main.js // 主方法
utils
根据个人习惯,首先我们 新建 util 文件夹,编写公用方法。
在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSRF等。
每次使用axios是都要把所有的配置写一遍很是麻烦且不方便维护。所以将axios封装一下方便使用。这是我用的一个简单的封装模板:
import axios from 'axios';
// 创建axios实例
const service = axios.create({
baseURL: process.env.BASE_API, // 最终 url = base url + request url
//process.env.BASE_API 在 config 下的 dev.env.js 中设置
//request url 后面说到
// withCredentials: true, // 跨域请求时发送Cookie
timeout: 5000 // //设置接口响应时间
})
//http request 拦截器
axios.interceptors.request.use(
config => {
// 可以对下面的数据进行操作
//config.method 请求类型
//config.url 请求路径
//config.headers 请求头
//config.data 请求数据
return config;
},
error => {
//发生错误时执行
return Promise.reject(err);
}
);
//http response 拦截器
axios.interceptors.response.use(
response => {
// 可以对下面的数据进行操作
//response.status 请求返回状态
//response.data 请求返回数据
return response;
},
error => {
//发生错误时执行
return Promise.reject(error)
}
)
/**
* 封装get方法
* @param url
* @param data
* @returns {Promise}
*/
export function fetch(url,params={}){
return new Promise((resolve,reject) => {
axios.get(url,{
params:params
})
.then(response => {
resolve(response.data);
})
.catch(err => {
reject(err)
})
})
}
/**
* 封装post请求
* @param url
* @param data
* @returns {Promise}
*/
export function post(url,data = {}){
return new Promise((resolve,reject) => {
axios.post(url,data)
.then(response => {
resolve(response.data);
},err => {
reject(err)
})
})
}
/**
* 封装patch请求
* @param url
* @param data
* @returns {Promise}
*/
export function patch(url,data = {}){
return new Promise((resolve,reject) => {
axios.patch(url,data)
.then(response => {
resolve(response.data);
},err => {
reject(err)
})
})
}
/**
* 封装put请求
* @param url
* @param data
* @returns {Promise}
*/
export function put(url,data = {}){
return new Promise((resolve,reject) => {
axios.put(url,data)
.then(response => {
resolve(response.data);
},err => {
reject(err)
})
})
}
//暴露接口
export default service
这是一个 包括了 request ;response 以及get ;post ;patch ;put 的简单 axios 封装。
我们现在只是登录,没有太多需求,就按照默认的来。
在 utils 文件夹下新建 js request.js 。用来封装 axios。代码:
import axios from 'axios'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
service.interceptors.request.use(
config => {
return config
},
error => {
console.log(error)
return Promise.reject(error)
}
)
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
//如果自定义代码不是20000,则判断为错误。
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})//提示错误
return Promise.reject(new Error(res.message || 'Error'))//返回一个错误
} else {
return res
}
},
error => {
return Promise.reject(error)
}
)
export default service
后台在验证登录成功后传回的是:
{
code: 20000,
data: {
token: "admin-token"
}
}
密码或密码错误则是:
{
code: 60204,
message: "Account and password are incorrect."
}
api
上面封装的 request 我们这里来使用。
我们来写登录方法。非常简单,只用调用上面的 request 即可。
在 api 文件夹 下新建 js 文件 user.js。
import request from '@/utils/request'
export function loginByUsername(data) {
return request({
url: 'admin/user/login', //上文的 request url
method: 'post', // 请求方式
data //请求数据
})
}
这样我们就后台登录我就完成了。后台的响应逻辑我们后面讲。
store
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式;集中存储和管理应用的所有组件状态。每一个Vuex应用的核心就是store(仓库)。store 基本上就是一个容器,包含着应用中大部分的state(状态)。
Vuex和单纯的全局对象有以下两点不同:
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
基本用法:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// 存放状态
},
getters: {
// state的计算属性
},
mutations: {
// 更改state中状态的逻辑,同步操作
},
actions: {
// 提交mutation,异步操作
},
// 如果将store分成一个个的模块的话,则需要用到modules。
//然后在每一个module中写state, getters, mutations, actions等。
modules: {
a: moduleA,
b: moduleB,
// ...
}
});
根据我们的登录需求。当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token。我们需要存储 token 使它在全部页面可用。
在 store 页面新建 js 文件 index.js。
import Vue from 'vue'
import Vuex from 'vuex'
import { loginByUsername } from '@/api/user'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// 存放状态
token: ''
},
getters: {
//获取到用户状态,//实时监听state值的变化(最新状态)
GetToken: state => state.token
},
mutations: {
// 更改state中状态的逻辑,同步操作
SET_TOKEN: (state, token) => {
state.token = token
}
},
actions: {
// 提交mutation,异步操作
LoginByUsername({ commit }, userInfo) {
const username = userInfo.username.trim()
return new Promise((resolve, reject) => {
loginByUsername({username : username,password: userInfo.password}).then(response => {
const data = response.data
commit('SET_TOKEN', data.token)//mutations中的方法
resolve()
}).catch(error => {
console.log('token err')
reject(error)
});
});
}
}
})
export default store
我们完成了登录的全部处理,现在开始写页面。
在 views/login 文件夹下新建vue文件 index.vue。
随便在页面撸上两个input的框,一个是登录账号,一个是登录密码。再放置一个登录按钮。我们将登录按钮上绑上click事件,点击登录之后调用 store 的登录方法提交账号和密码进行验证。
<template>
<div>
<input type="text" v-model="loginForm.username" placeholder="用户名" />
<input type="text" v-model="loginForm.password" placeholder="密码" />
<button @click="handleLogin">登录</button>
</div>
</template>
<script>
export default {
name: 'Login',
data () {
return {
loginForm: {
username: 'admin',
password: '111111'
}
}
},
methods: {
handleLogin: function () {
console.log('login' + this.loginForm)
// this.$store.dispatch('LoginByUsername') 调用 store 下 actions 中的LoginByUsername方法。
this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
this.$router.push({
path: '/'
}) // 登录成功之后重定向到首页
}).catch((err) => {
console.log(err) // 登录失败提示错误
})
}
}
}
</script>
一个简单的登录页面完成。
layout
这里应该放导航栏,现在只是简单的做一个登录 没有必要写太复杂,简单的写一下就行。
在 layout 文件夹下 新建 vue 文件 index.vue。
<template>
<div> layout</div>
</template>
<script>
</script>
<style>
</style>
就这么简单就ok了。
router
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
import Foo from '../views/foo'
// 每个路由应该映射一个组件。 其中"component" 可以是通过 Vue.extend() 创建的组件构造器,或者,只是一个组件配置对象。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: () => import('@/views/AMap')}
]
//创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
routes: routes
})
我们简单的编写一下路由。
在 router 文件夹下 新建 js 文件 index.js。
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
import Layout from '@/layout'
import Login from '../views/login/';
export const constantRouterMap = [
{ path: '/login', component: Login },
{ path: '/',component: Layout },
]
export default new Router({
routes: constantRouterMap
});
main.js
我们在全局钩子router.beforeEach中拦截路由,判断是否已获得token,如果没有则进入login页面。
router.beforeEach((to, from, next) => {
// do something
next();
});
每个钩子方法接收三个参数:
to: Route: 即将要进入的目标路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
我们修改main.js 文件:
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import store from './store'
Vue.config.productionTip = false
const whiteList = ['/login']
router.beforeEach(async (to, from, next) => {
const hasToken = store.getters.GetToken
console.log('getToken' + hasToken)
if (!hasToken) {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(`/login`)
}
} else {
next()
}
})
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store, //引入到vue中
components: {
App
},
template: '<App/>'
})
运行
所有的编写就完成了。可以运行查看了。
在运行前 先 install 一下。
进入页面:
账号或密码错误提示:
登录成功页面:
其他
如果 selint 报错。可以在 config/inedx.js 中 useEslint 设为 false。
后端框架可以使用 Spring Boot;.NET ;Flask。 编写。当然,最好使用 mock。
mock 逻辑:
const tokens = {
admin: {
token: 'admin-token'
},
editor: {
token: 'editor-token'
}
}
module.exports = [
// user login
{
url: 'admin/user/login',
type: 'post',
response: config => {
const { username } = config.body
const token = tokens[username]
// mock error
if (!token) {
return {
code: 60204,
message: 'Account and password are incorrect.'
}
}
return {
code: 20000,
data: token
}
}
}]
仅仅 mock 登录逻辑,具体实现请完善。