效果图
安装
npm install mockjs --save // 三选一就可以了
cnpm install mockjs --save
yarn add mockjs
新建mock目录
mock
目录下新建index.js
和json
文件
mock
下的index
文件,注意每改接口后都要重启
才能生效
// 引入外部资源
const fs = require('fs')
const path = require('path')
const Mock = require('mockjs')
// 读取本地文件
function getJsonFile (filePath) {
// 读取指定的json文件
const json = fs.readFileSync(path.resolve(__dirname, filePath), 'utf-8')
// 解析并返回
return JSON.parse(json)
}
module.exports = function(app) {
// 登录
app.post('/login', (req, res) => {
const users = getJsonFile('./users.json') // 响应时,返回 mock data的json数据
res.json(Mock.mock(users)) // 将json传入 Mock.mock 方法中,生成的数据返回给浏览器
})
// 城市列表
app.get('/citylist', (req, res) => {
const city = getJsonFile('./city.json')
res.json(Mock.mock(city))
})
}
mock
下的users.json
文件
{
"status": true,
"message": "登陆成功",
"data": [
{
"id": 1,
"username": "admin",
"password": "123",
"nickname": "半度℃"
},
{
"id": 2,
"username": "tom",
"password": "123",
"nickname": "Tom"
}
]
}
新建api目录
api
目录下新建index.js
和request.js
文件
api
下的index.js
文件
import request from './request';
export default {
login(params) {
return request.post('/login', params, {
action: '登录'
});
},
city(params) {
return request.get('/citylist', {
action: '获取城市列表',
params
});
}
}
api
下的request.js
文件,这里也是对 axios
的封装
import axios from 'axios';
import Vue from 'vue';
import store from '@/store/index'
// 取消请求(用于当没有 token 时,取消当前的请求,直到获取 token)
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
// 过滤掉空字符串
function filterEmptyKey(params) {
Object.keys(params).forEach(key => {
if (
params[key] === null ||
params[key] === '' ||
params[key] === 'null' ||
params[key] === undefined
) {
delete params[key];
}
});
}
// 环境的切换
let baseURL = null
if (process.env.NODE_ENV === 'development') {
baseURL = 'http://localhost:8080';
} else if (process.env.NODE_ENV === 'debug') {
baseURL = '';
} else if (process.env.NODE_ENV === 'production') {
baseURL = 'http://bandu.com';
}
const service = axios.create({
baseURL: baseURL,
timeout: 30000,
cancelToken: source.token
});
// 请求拦截
service.interceptors.request.use(
(config) => {
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,
// 不用每次请求都手动添加了
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
const token = store.state.token;
token && (config.headers.Authorization = token);
if (config.method === 'post') {
const params = {
...config.data
};
filterEmptyKey(params); // 过滤掉空字符串
config.headers['Content-Type'] = 'application/json';
config.data = params;
} else if (config.method === 'get') {
config.params = {
_t: Date.parse(new Date()) / 1000,
...config.params
};
filterEmptyKey(config.params); // 过滤掉空字符串
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截
service.interceptors.response.use(
(res) => {
const { data, config } = res
// 如果是下载文件则需要加上这一段类型判断
if (config.responseType === 'blob') {
return response
}
if (!data.status) {
// 错误提醒
let message = data.message ? data.message : '未知错误'
Vue.prototype.$message({
message: config.action + '失败:' + message,
type: 'error'
});
}
return Promise.resolve(data) || {};
},
(error) => {
return Promise.reject(error);
}
);
export default service;
配置 vue.config.js
重点是在devServer
里配置before
,注意配置好之后一定要重启
// 配置目录别名
const path = require('path');
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
publicPath: '/',
// 输出文件目录
outputDir: 'dist',
// 静态资源目录 (js, css, img, fonts)
assetsDir: 'assets',
// 指定生成的 index.html 的输出路径
indexPath: 'index.html',
// lintOnSave:{ type:Boolean default:true } 是否使用eslint
lintOnSave: true,
// productionSourceMap:{ type:Bollean,default:true } 生产源映射
// 如果不需要生产时的源映射,那么将此设置为 false 可以加速生产构建
productionSourceMap: true,
// 默认情况下 babel-loader 会忽略所有 node_modules 中的文件。
// 如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来
transpileDependencies: [],
// alias 目录别名配置
chainWebpack: (config) => {
config.resolve.alias.set('@', resolve('src'))
},
css: {
loaderOptions: {
css: {
// 这里的选项会传递给 css-loader
},
sass: {
prependData: `@import "~@/assets/css/custom.scss";` // 全局引入 scss 变量, 不用可以去掉
},
postcss: {
// 这里的选项会传递给 postcss-loader
}
}
},
// devServer: { type: Object } 3个属性 host, port, https, 它支持 webpack-dev-server 的所有选项
devServer: {
port: 8080,
open: false, // 配置自动启动浏览器
overlay: { // 浏览器 overlay 同时显示警告和错误
warnings: true,
errors: true
},
before: require('./public/mock') /** 重点是这里,一定不能忘记 **/
},
configureWebpack: {
devtool: process.env.NODE_ENV === 'development' ? 'eval-source-map' : undefined,
// 解决缓存问题
output: {
filename: `js/[name].${new Date().getTime()}.js`,
chunkFilename: `js/[name].${new Date().getTime()}.js`
}
}
}
在组件中使用
<template>
<div class="wrapper">
<el-form :model="ruleForm" ref="ruleForm" :rules="rules" label-width="80px">
<el-form-item prop="username" label="用户名">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input v-model="ruleForm.password"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="resetForm('ruleForm')">重置</el-button>
<el-button type="primary" @click="submitForm('ruleForm')">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import loginApi from '@/api/index';
export default {
data () {
return {
ruleForm: {
username: '',
password: ''
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] }
]
}
}
},
methods: {
submitForm (formName) {
const that = this
this.$refs[formName].validate((valid) => {
if (valid) {
loginApi.login().then((res) => {
const result = res.data ? res.data : []
let flag = false
for (var i = 0; i < result.length; i++) {
if (
result[i].username === that.ruleForm.username &&
result[i].password === that.ruleForm.password
) {
flag = true
// 将符合条件的用户ID和昵称存储到本地
sessionStorage.userId = result[i].id
sessionStorage.nickname = result[i].nickname
}
}
if (flag) {
that.$router.push('/home/')
}
})
}
})
},
resetForm (formName) {
this.$refs[formName].resetFields() // 重置用户名和密码
}
}
}
</script>
<style scoped>
.wrapper {
width: 340px;
border: 1px solid #cccccc;
border-radius: 5px;
padding: 50px;
position: absolute;
top: 200px;
left: 50%;
transform: translateX(-50%);
}
</style>