这里准备采用的技术栈为:vue全家桶+element-ui 这里由于是后台管理系统,没有作SSR的必要。因此这里就采用先后端分离来昨晚这个项目~javascript
vue init webpack gwc_manage
复制代码
初始化过程当中,会让咱们进行一些依赖包,项目配置的基本选择。项目名称,做者信息,描述,是否安装路由,eslint,测试单元,npm/yarn。 这里不选择安装eslint和测试单元。用过的人应该知道很是酸爽。 项目初始化成功后,css
npm run dev
复制代码
npm install less less-loader -s
复制代码
注:后面的-s表示本地安装-g表示全局安装,不些则表示默认本地安装 由于使用了less后,我的通常喜欢将整个项目的配色进行一个管理。因此须要进行全局的配置。 这里安装完成后,继续安装html
npm install sass-resources-loader --save-dev
复制代码
接下来,咱们找到build文件夹下的utils文件 exports.cssLoaders = function (options) {}中加上一下代码:前端
function lessResourceLoader() {
var loaders = [
cssLoader,
'less-loader',
{
loader: 'sass-resources-loader',
options: {
resources: [
path.resolve(__dirname, '../src/assets/styles/common.less'),
]
}
}
];
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
复制代码
注意位置必定要加对,且记得把path.resolve(__dirname,'../src/assets/styles/common.less')路径改为本身对应的文件。而后后面将 return{} 块中的 less: generateLoaders('less') 替换成上面自定义的函数 less: lessResourceLoader(); 修改完配置文件记得重启服务器:npm run dev 给你们上图看一下主要配置: image-20190513172125756.png vue
@bg-blue: #3576e0;
复制代码
在App.vue中修改java
<style lang="less" scoped>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
background-color: @bg-blue;
}
</style>
复制代码
而后保存,打开浏览器。就能够看到咱们配置好的全局变量被成功引用 image-20190513172403241.png webpack
项目搭建好了,可是能够看到一些基本的样式须要咱们进行更改ios
assets文件夹下styles文件中新建base.less文件。百度搜索reset.css而后复制文中的基本样式,并添加html,body宽高为100%便可。web
新建function.less文件css样式工具封装在文中找到适合本身项目的文件复制并粘贴金function.less文件。vue-router
新建index.css文件做为出口文件。文件内容以下:
@import './base.less'; @import './function.less'; 在main.js文件中引入index.css文件
至此引入的基本的样式文件就算被全局引用了。
至于封装的function样式类,咱们只须要在须要的时候,选择性的 添加类名便可。
npm install element-ui -s
复制代码
安装完成后,在main.js中引入
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI);
复制代码
安装完成后,修改App.vue:
<template>
<div id="app">
<img src="./assets/logo.png">
<el-button type="danger">测试Element</el-button>
<router-view/>
</div>
</template>
复制代码
打开浏览器,咱们能够看到button按钮是已经生效了。 image-20190513174650403.png
在src文件夹下新建utils文件夹新建http.js为咱们的请求方法的文件 image-20190513174918109.png
这里借助两个依赖包 axios和vue-cookies
npm install axios vue-cookies -s
复制代码
这里封装三个类型的。get请求一个post,json格式的一个post,body格式的一个
import axios from 'axios'
import Vue from 'vue'
// 请求方式的配置
export const postJsonRequest = (url, params) => {
return axios({
method: 'post',
url: url,
data: params,
headers: {
'Content-Type': 'application/json',
},
});
}
export const postRequest = (url, params) => {
return axios({
method: 'post',
url: url,
data: params,
transformRequest: [function (data) {
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}],
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
}
export const getRequest = (url, data = {}) => {
return axios({
method: 'get',
params: data,
url: url,
});
}
复制代码
这个就是请求方法的配置
随后咱们在utils目录下新建index.js文件做为工具文件夹的出口文件
utils>index.js:
引入封装的http请求并挂在到Vue原型链上供全局使用
import * as http from './http';
const install = (Vue, opts = {}) => {
if (install.installed) return;
Vue.prototype.$http = http;
}
export default install
main.js
import Utils from './utils';
Vue.use(Utils);
复制代码
这里方法请求的封装就算是完成了。
拦截器的配置
拦截器:登陆状态的管理,以及每次请求时,咱们作的一些处理。对不一样code状态作的不一样的处理
拦截器分为请求拦截器和响应拦截器。字面意思可理解为:请求时的拦截和响应时的拦截
import axios from 'axios'
import { Message } from 'element-ui'
import router from 'vue-router'
import Vue from 'vue'
import VueCookies from 'vue-cookies'
// 请求拦截
axios.interceptors.request.use(config => {
if (VueCookies.isKey('isLogin')) {
const token = getToken(config);
config.headers['token'] = token;
}
return config;
}, err => {
Message.error({
message: '请求超时!'
});
return Promise.resolve(err);
})
// 响应拦截
axios.interceptors.response.use(res => {
switch (res.data.code) {
case 200:
return res.data.result;
case 401:
Message.error({
message: res.data.message
});
router.push('/login');
VueCookies.remove('userinfo');
return Promise.reject(res);
case 201:
Message.error({
message: res.data.message
});
case 403:
Message.warning({
message: res.data.message
});
return Promise.reject(res);
default:
return Promise.reject(res);
}
}, err => {
if (!err.response) {
return false;
}
switch (err.response.status) {
case 500:
Message.error({
message: '服务器出小差了⊙﹏⊙∥'
});
break;
case 504:
Message.error({
message: '服务器被吃了⊙﹏⊙∥'
});
break;
case 404:
Message.error({
message: '服务器被吃了⊙﹏⊙∥'
});
break;
case 403:
Message.error({
message: '权限不足,请联系管理员!'
});
break;
default:
Message.error({
message: '网络超时'
});
}
return Promise.reject(err);
})
复制代码
Vue拦截器的基本配置也就是以上这些了。下来我的想要实现一个loading的局部加载。就要借助计数器了。
为何说要作一个计数器?
由于在同一个view中可能会有不少个请求,咱们总不可能在一个请求完成后就关闭loading的加载吧?这样显然不合适。因此咱们须要计时器去将全部的请求记录起来。而后在全部的请求完成后关闭loading。
//请求时loading配置
var loading;
function startLoading() {
loading = Vue.prototype.$loading({
lock: true,
text: "Loading...",
background: 'rgba(0, 0, 0, 0.5)',
target: document.querySelector('.loading-area') //设置加载动画区域
});
}
function endLoading() {
loading.close();
}
var needLoadingRequestCount = 0;
//开启loading并计数
function showFullScreenLoading() {
if (needLoadingRequestCount === 0) {
startLoading();
}
needLoadingRequestCount++;
};
//计数器==0关闭loading
function tryHideFullScreenLoading() {
if (needLoadingRequestCount <= 0) return;
needLoadingRequestCount--;
if (needLoadingRequestCount === 0) {
endLoading();
}
};
复制代码
loading的依赖在element安装后是直接挂在到了vue的原型链上。咱们再也不须要作过多处理。这里计数器完成。下来咱们只须要在请求的时候调用,在响应的时候或者请求报错的时候调用关闭的方法便可。
须要注意的是要实现局部的加载,咱们就须要在响应实现loading的地方添加类型loading-area才能够!!!
http.js全部代码以下:
/** * @description 配置网络请求 * @author luokaibin chaizhiyang */
import axios from 'axios'
import { Message } from 'element-ui'
import router from 'vue-router'
import Vue from 'vue'
import VueCookies from 'vue-cookies'
axios.defaults.timeout = 300000; // 请求超时5fen
//请求时loading配置
var loading;
function startLoading() {
loading = Vue.prototype.$loading({
lock: true,
text: "Loading...",
background: 'rgba(0, 0, 0, 0.5)',
target: document.querySelector('.loading-area') //设置加载动画区域
});
}
function endLoading() {
loading.close();
}
var needLoadingRequestCount = 0;
function showFullScreenLoading() {
if (needLoadingRequestCount === 0) {
startLoading();
}
needLoadingRequestCount++;
};
function tryHideFullScreenLoading() {
if (needLoadingRequestCount <= 0) return;
needLoadingRequestCount--;
if (needLoadingRequestCount === 0) {
endLoading();
}
};
// 请求拦截
axios.interceptors.request.use(config => {
showFullScreenLoading();
if (VueCookies.isKey('userinfo')) {
const token = getToken(config);
config.headers['token'] = token;
}
return config;
}, err => {
tryHideFullScreenLoading();
Message.error({
message: '请求超时!'
});
return Promise.resolve(err);
})
// 响应拦截
axios.interceptors.response.use(res => {
tryHideFullScreenLoading();
switch (res.data.code) {
case 200:
return res.data.result;
case 401:
Message.error({
message: res.data.message
});
router.push('/login');
// VueCookies.remove('userinfo');
return Promise.reject(res);
case 201:
Message.error({
message: res.data.message
});
case 403:
Message.warning({
message: res.data.message
});
return Promise.reject(res);
default:
return Promise.reject(res);
}
}, err => {
tryHideFullScreenLoading();
if (!err.response) {
return false;
}
switch (err.response.status) {
case 500:
Message.error({
message: '服务器出小差了⊙﹏⊙∥'
});
break;
case 504:
Message.error({
message: '服务器被吃了⊙﹏⊙∥'
});
break;
case 404:
Message.error({
message: '服务器被吃了⊙﹏⊙∥'
});
break;
case 403:
Message.error({
message: '权限不足,请联系管理员!'
});
break;
default:
Message.error({
message: '网络超时'
});
}
return Promise.reject(err);
})
// 请求方式的配置
export const postJsonRequest = (url, params) => {
return axios({
method: 'post',
url: url,
data: params,
headers: {
'Content-Type': 'application/json',
},
});
}
export const postRequest = (url, params) => {
return axios({
method: 'post',
url: url,
data: params,
transformRequest: [function (data) {
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}],
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
}
export const getRequest = (url, data = {}) => {
return axios({
method: 'get',
params: data,
url: url,
});
}
复制代码
至此整个的全局loading,拦截器,fetch方法的封装就算完成了。
element-ui的使用,咱们要多借助官方文档去灵活使用。而不是死记硬背。太多的ui框架,再强的大脑也背不过来,况且,为了本身的头发着想一下啦~
这里并未设置跨域访问。你们能够根据vue的代理。这样也是能够进行跨域访问的。详见个人这篇博文Vue设置代理进行跨域访问,不过这篇文章是针对vue3.0设置,2.0的设置文件,为config下的index.js文件。固然前提是后端接口也进行跨域的设置,不然单纯的前端设置是没有做用的。
局部loading的使用,要求有一个好的布局基础。不然,在后期的使用过程当中,会有喝多样式错乱的问题。
请求方式有不少种,还有下载文件的请求配置,delete,post,put等等你们可根据本身需求灵活配置。最终的使用this.$http. get/post/put/...等等就能够进行调用
底层布局(Layout),路由(Router),状态库(vuex),Github仓库关联。
敬请期待~