哈嘍你們好,這裏是代碼搬運工。第一次寫還挺緊張的呀。
javascript
首先咱們安裝一個vue-cli(不會的同窗能夠看這裏npm安裝vue)css
如今咱們的目錄是這樣的(eslint我沒開): html
而後裝好依賴啓動這個cli在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的改造結束。