哈喽你们好,这里是代码搬运工。第一次写还挺紧张的呀。
javascript
首先咱们安装一个vue-cli(不会的同窗能够看这里npm安装vue)css
如今咱们的目录是这样的(eslint我没开): html
在router文件夹新建一个asyncload.js
代码以下:vue
export default function (url) {
return () => System.import(`@/${url}`)
}
export const asyncImport = (url) => {
return () => import(`@/${url}`)
}
复制代码
这里导出两个懒加载的方法System.import
和import()
这两个方法均可以作路由懒加载,System在webpack2.0文档中说明已经废弃 可是到如今仍是能用的,import是vue-router官方推荐的方法,同窗们能够自由选择。固然import()
还须要一个babel插件syntax-dynamic-import
,请安装babel-plugin-syntax-dynamic-import
并修改.babelrc
plugins里加入syntax-dynamic-import
修改router为懒加载的方式java
import Vue from 'vue'
import Router from 'vue-router'
import asyncLoad,{asyncImport} from './asyncload'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
// component: asyncImport('components/HelloWorld.vue') //两种方式均可以
component: asyncLoad('components/HelloWorld.vue')
}
]
})
复制代码
从新启动cli一切正常咱们的懒加载已经成功webpack
在config文件夹的index文件proxyTable
属性上加入下面代码ios
proxyTable: {
'/api': {
target: 'http://localhost:3000', // 接口的域名
// secure: false, // 若是是https接口,须要配置这个参数
changeOrigin: true, // 若是接口跨域,须要进行这个参数配置
pathRewrite: {
'^/api': ''
}
}
}
复制代码
这样咱们就把接口代理的配置弄好了,而后在src目录下新建common文件夹,这这个文件夹下新建baseurl.js
、code.js
、constant.js
、url.js
这4个配置的js文化
baseurl.js
:ajax的基础路径git
//开发环境添加/api前缀
export default process.env.NODE_ENV === 'development' ? '/api' : ''
复制代码
code.js
:ajax状态码github
//同窗们能够和后台协商添加上本身的
const SUCCESS = ['S0000','S0001']
const ABNORMAL = ['A0000']
const LOGIN_OUT=['U0000']
const ERROR = ['E0000']
export {SUCCESS, ABNORMAL, ERROR,LOGIN_OUT}
复制代码
constant.js
:vuex用的web
const USER_INFO = 'USER_INFO'
const LOADING='LOADING'
export {USER_INFO,LOADING}
复制代码
url.js
:后台接口路径统一在这里管理
//假若有个登陆请求
const LOGIN_URL='/login'
export {
LOGIN_URL
}
复制代码
好了全部的配置工做都完成了
在src目录下新建文件夹network在network新建api文件夹在api文件夹下新建BaseApi.js
用来接管axios代码以下:
import axios from 'axios'
import Qs from 'qs'
import BASE_URL from '../../common/config/baseurl'
class BaseApi {
static isinIt=false;
constructor () {
this.createAxios();
this.initNotice()
}
createAxios () {
if (BaseApi.isinIt) {
return this.axios=BaseApi.isinIt
}
let api = axios.create({
// 请求的接口,在请求的时候,如axios.get(url,config);这里的url会覆盖掉config中的url
url: '',
// 请求方法同上
method: 'post', // default
// 基础url前缀
baseURL: BASE_URL,//baseurl.js里面定义的前缀
transformRequest: [function (data) {
// 这里能够在发送请求以前对请求数据作处理,好比form-data格式化等,这里可使用开头引入的Qs(这个模块在安装axios的时候就已经安装了,不须要另外安装)
data = Qs.stringify(data)
return data
}],
// paramsSerializer: function(params) {
//
// },
transformResponse: [function (data) {
// 这里提早处理返回的数据
try {
return JSON.parse(data)
} catch (e) {
return data
}
}],
// 请求头信息
headers: {
},
// parameter参数
params: {
},
// post参数,使用axios.post(url,{},config);若是没有额外的也必需要用一个空对象,不然会报错
data: {
},
// 设置超时时间
timeout: 5000,
// 返回数据类型
responseType: 'json', // default
})
api.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
api.interceptors.request.use(
(config) => {
return this.request(config)
}, (err) => {
return this.reject(err)
})
api.interceptors.response.use(
(response) => {
return this.response(response)
},
(error) => {
return this.reject(error)
}
)
BaseApi.isinIt=this.axios = api
}
request () {
throw Error('必须实现request函数!!!')
}
response () {
throw Error('必须实现response函数!!!')
}
reject () {
throw Error('必须实现reject函数!!!')
}
initNotice () {
throw Error('必须实现通知函数!!!')
}
}
export default BaseApi
复制代码
解释一下BaseApi是一个抽象类,它使用静态属性保证axios值初始化一次,并接管全部的拦截器方法和初始化一个通知方法,这个类不实现这些方法,把实现的任务交给子类,这样可保证扩展性。剩下的咱们就完成一个BaseApi的子类来实现这些方法,因此咱们新建一个Api.js
import BaseApi from './BaseApi'
import {ABNORMAL, LOGIN_OUT, SUCCESS} from '../../common/config/code'
import {Notice} from 'iview'
class Api extends BaseApi {
constructor() {
super()
}
initNotice() {
this.Notice = Notice
// dosomething
}
request(config) {
return config;
}
response(response) {
return response;
}
reject(error) {
console.error(error)
}
//用户未登陆
loginOut() {
App.$router.push({
name: 'login'
})
}
before() {
}
after() {
}
abnormal(param, res) {
this.showNotice(param, res, '舒适提示', 'warning')
}
error(param, res) {
this.showNotice(param, res, '很差了', 'error')
}
showNotice(param, res, title, type = 'info') {
this.Notice[type]({
title,
render: param.render ? param.render(...res) : h => {
return h('span', [
res.message,
])
}
})
}
async common(param) {
let _config = Object.assign({}, param)
await this.before()
let res;
try {
let result = await this.axios(param.url, _config)
res = (result && result.data) ? result.data : null;
if (!res.data||!res.state || ABNORMAL.includes(res.state)) {
param.abnormal ? param.abnormal(param, res) : this.abnormal(param, res)
} else if (LOGIN_OUT.includes(res.state)) {
this.loginOut();
} else if (SUCCESS.includes(res.state)) {
(param.successNotice) ? this.showNotice(param, res, '恭喜你', 'success') : '';
param.success ? param.success(res) : ''
} else {
param.error ? param.error(res, param) : this.error(param, {message: "程序在开小差"})
}
} catch (e) {
console.error(e);
param.error ? param.error(res, param) : this.error(param, {message: "程序在开小差"})
}
await this.after()
return res
}
}
export default Api
复制代码
Api.js
的主要工做就是完成BaseApi.js
的抽象方法并实现一个common的ajax通用方法,而且定义两个抽象环绕方法before
和after
以供每一个子类实现(好比统一的loading),而且在根据后台的状态码返回对应的方法。common
方法接受一个参数params里面除了axios
须要的参数外还有successNotice
(成功的时候是否显示通知)、error
(ajax失败的时候调用的方法)、abnormal
(ajax出现异常的是调用的方法)、success
(请求成功的时候调用的方法)这些方法都会覆盖子类的配置(参数配置优先)这样咱们就完成了axios二次封装和统一ajax异常处理
在作全局loading以前咱们先把vuex集成进来(关于vuex的配置我就不贴了有心去的能够去看看个人配置vuex配置),在state里面新建一个loading的状态。
<template>
<section class="mark">
<div class="loader"></div>
</section>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.mark{
top:0;
position: fixed;
height: 100vh;
width: 100vw;
background: white;
}
.loader {
position: relative;
width: 2.5em;
height: 2.5em;
transform: rotate(165deg);
}
.loader:before, .loader:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
display: block;
width: 0.5em;
height: 0.5em;
border-radius: 0.25em;
transform: translate(-50%, -50%);
}
.loader:before {
animation: before 2s infinite;
}
.loader:after {
animation: after 2s infinite;
}
@keyframes before {
0% {
width: 0.5em;
box-shadow: 1em -0.5em rgba(225, 20, 98, 0.75), -1em 0.5em rgba(111, 202, 220, 0.75);
}
35% {
width: 2.5em;
box-shadow: 0 -0.5em rgba(225, 20, 98, 0.75), 0 0.5em rgba(111, 202, 220, 0.75);
}
70% {
width: 0.5em;
box-shadow: -1em -0.5em rgba(225, 20, 98, 0.75), 1em 0.5em rgba(111, 202, 220, 0.75);
}
100% {
box-shadow: 1em -0.5em rgba(225, 20, 98, 0.75), -1em 0.5em rgba(111, 202, 220, 0.75);
}
}
@keyframes after {
0% {
height: 0.5em;
box-shadow: 0.5em 1em rgba(61, 184, 143, 0.75), -0.5em -1em rgba(233, 169, 32, 0.75);
}
35% {
height: 2.5em;
box-shadow: 0.5em 0 rgba(61, 184, 143, 0.75), -0.5em 0 rgba(233, 169, 32, 0.75);
}
70% {
height: 0.5em;
box-shadow: 0.5em -1em rgba(61, 184, 143, 0.75), -0.5em 1em rgba(233, 169, 32, 0.75);
}
100% {
box-shadow: 0.5em 1em rgba(61, 184, 143, 0.75), -0.5em -1em rgba(233, 169, 32, 0.75);
}
}
.loader {
position: absolute;
top: calc(50% - 1.25em);
left: calc(50% - 1.25em);
}
</style>
</script>
<style>
#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;
}
</style>
复制代码
而后新建components\base\loading\Loading.vue
而后咱们在App.vue
引入而后和路由同级
<template>
<div id="app"> <img src="./assets/logo.png"> <router-view v-if="!loading"/> <loading v-else></loading> </div> </template> <script> import {mapGetters} from 'vuex' import Loading from '@/components/base/loading/Loading.vue' export default { name: 'App', computed:{ ...mapGetters([ 'loading' ]) }, components:{ Loading } } </script> <style> #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; } </style> 复制代码
新建network\api\imp\
里面新建一个api实现类UserApi.js
继承与Api.js
并实现环绕before
和after
<template>
<div class="hello">
这里是模拟的axios测试页帐号是admin123密码是111111
这是login页<br>
<div v-if="!user">
帐号<input type="text" v-model="userName"><br>
密码<input type="text" v-model="pwd"><br>
<button @click="login">登陆</button>
<button @click="login3">带通知的登陆</button>
</div>
<div v-else>
<button @click="out">退出登陆</button>
</div>
<div>
展现异常和错误处理
<button @click="login1">异常通用</button>
<button @click="login2">自定义异常</button>
</div>
</div>
</template>
<script>
import UserApi from '@/network/api/imp/UserApi.js'
export default {
name: 'Login',
data () {
return {
user:null,
userName:"",
pwd:"",
}
},
async created(){
},
mounted(){
},
methods:{
async login(){
//既能够等待api执行完得到数据
const data=await UserApi.login({
data:{
userName:this.userName||'admin',
password:this.pwd||'111111',
},
//也能够在回调函数里面得到数据
success:(res)=>{
}
})
console.info(data)
},
async login1(){
await UserApi.login({
data:{
userName:this.userName||'admin2',
password:this.pwd||'111111',
}
})
},
async login2(){
await UserApi.login({
data:{
userName:this.userName||'admin1',
password:this.pwd||'111111',
},
abnormal(){
alert('我是自定义的异常处理')
}
})
},
async login3(){
await UserApi.login({
data:{
userName:this.userName||'admin',
password:this.pwd||'111111',
},
successNotice:true
})
},
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
input{
border: 1px solid black;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
list-style: none;
}
a {
color: #42b983;
}
</style>
复制代码
并在根目录下新建一个测试服务器serverk开启一个测试服务器
const Koa = require('koa2')
const app = new Koa()
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser');
let router = new Router()
const main = Context => {
let {userName,password}=Context.request.body
if(userName==='admin'&&password==='111111'){
return Context.body={
data:{
token:'57af5b10-3a76-11e5-922a-75f42afeee38',
name:'代码搬运工',
userName,
},
state:'S0001',
message:'登陆成功'
}
}else if(userName!=='admin'){
return Context.body={
data:{},
state:'A0001',
message:'用户名不正确'
}
}else if(password!=='111111'){
return Context.body={
data:{},
state:'A0001',
message:'密码不正确'
}
}
};
// router.post('/login', main)
app.use(bodyParser());
router.post('/login', main)
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000, () => {
console.log('[demo] route-use-middleware is starting at port 3000')
})
复制代码
最后的效果就是
到这一步vue-cli的改造基本上是完了,可是仍是缺了点什么咱们能够利用装饰器对api的实现层就行进一步改造咱们先安装如下装饰的依赖并把babel-plugin-transform-decorators
和babel-plugin-transform-decorators-legacy
,并在.babelrc
plugins里面引入"plugins": ["transform-vue-jsx", "transform-runtime","syntax-dynamic-import","transform-decorators-legacy"],
,对Api进行改造
import BaseApi from './BaseApi'
import {ABNORMAL,LOGIN_OUT,SUCCESS} from '../../common/config/code'
import {Notice} from 'iview'
import {symbolContext} from '../../decorator/decorator'
class Api extends BaseApi {
constructor (target) {
super()
if(target){
this.context.call(this,target)
}
}
//因为装饰获得的是Api这个类而不是实例咱们须要一些特殊的方法来实现
context(target){
target.prototype[symbolContext]=this
}
initNotice () {
this.Notice=Notice
// dosomething
}
request (config) {
return config;
}
response (response) {
return response;
}
reject (error) {
console.error(error)
}
//用户未登陆
loginOut(){
App.$router.push({
name:'login'
})
}
before () {}
after () {}
abnormal (param,res) {
this.showNotice(param,res,'舒适提示','warning')
}
error (param,res) {
this.showNotice(param,res,'很差了','error')
}
showNotice(param,res,title,type='info'){
this.Notice[type]({
title,
render:param.render?param.render(...res):h=>{
return h('span', [
res.message,
])
}
})
}
async common (param) {
console.info(param)
let _config = Object.assign({}, param)
await this.before()
let res;
try {
res = await this.axios(param.url, _config)
res=(res&&res.data)?res.data:null;
if (!res.data||!res.state || ABNORMAL.includes(res.state)) {
param.abnormal ? param.abnormal(param,res) : this.abnormal(param,res)
}else if (LOGIN_OUT.includes(res.state)){
this.loginOut();
} else if(SUCCESS.includes(res.state)){
(param.successNotice)?this.showNotice(param,res,'恭喜你','success'):'';
param.success?param.success(res):''
}else{
param.error ? param.error(res,param) : this.error(param,{message:"程序在开小差"})
}
} catch (e) {
console.error(e);
param.error ? param.error(res,param) : this.error(param,{message:"程序在开小差"})
}
await this.after()
return res
}
}
export default Api
复制代码
对UserApi.js
进行改造
import Api from '../Api'
import {controller,post,get} from "../../../decorator/decorator";
import {LOGIN_URL} from '../../../common/config/url'
@controller('')
class UserApi extends Api{
constructor(){
super(UserApi);
}
before(){
App.$store.dispatch('changeLoading',true)
}
after(){
return new Promise(resolve=>{
setTimeout(()=>{
resolve( App.$store.dispatch('changeLoading',false))
},2000)
})
}
@post(LOGIN_URL,true)
async login(params){
return await this.common(params)
}
}
export {UserApi}
export default new UserApi()
复制代码
新建src\decorator\decorator.js
export const symbolPrefix = Symbol('prefix')
export const symbolContext = Symbol('context');
export function controller(path) {
return (target)=>{
target.prototype[symbolPrefix] =path;
target.prototype[symbolContext] =null;
}
}
function baseMethods(target, key, descriptor,name,path,successNotice) {
let method = descriptor.value;
descriptor.value = async (arg)=>{
arg.successNotice=successNotice
arg.url = target[symbolPrefix]?target[symbolPrefix]+path:path;
arg.method=name;
return await method.call(target[symbolContext],arg)
}
}
export function get(path,successNotice) {
return function (target, key, descriptor) {
baseMethods(target, key, descriptor,'get',path,successNotice)
}
}
export function post(path,successNotice) {
return function (target, key, descriptor) {
baseMethods(target, key, descriptor,'post',path,successNotice)
}
}
复制代码
controller为api的路径前缀,post为ajax为axios的发送方式,里面接受两个参数ajax路径和是否在成功的时候显示通知,改造后在测试哦一切如常完美。对vue-cli的改造结束。