爲Vue-Cli添磚加瓦

哈嘍你們好,這裏是代碼搬運工。第一次寫還挺緊張的呀。
javascript

首先咱們安裝一個vue-cli(不會的同窗能夠看這裏npm安裝vuecss

如今咱們的目錄是這樣的(eslint我沒開): html

而後裝好依賴啓動這個cli

1.路由懶加載

在router文件夾新建一個asyncload.js 代碼以下:vue

export default function (url) {
  return () => System.import(`@/${url}`)
}
export const asyncImport = (url) => {
  return () => import(`@/${url}`)
}
複製代碼

這裏導出兩個懶加載的方法System.importimport()這兩個方法均可以作路由懶加載,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

2.開發環境接口代理

在config文件夾的index文件proxyTable屬性上加入下面代碼ios

proxyTable: {
      '/api': {
        target: 'http://localhost:3000', // 接口的域名
        // secure: false, // 若是是https接口,須要配置這個參數
        changeOrigin: true, // 若是接口跨域,須要進行這個參數配置
        pathRewrite: {
          '^/api': ''
        }
      }
    }
複製代碼

這樣咱們就把接口代理的配置弄好了,而後在src目錄下新建common文件夾,這這個文件夾下新建baseurl.jscode.jsconstant.jsurl.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
}
複製代碼

好了全部的配置工做都完成了

3.axios2次封裝和統一ajax異常處理

在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通用方法,而且定義兩個抽象環繞方法beforeafter以供每一個子類實現(好比統一的loading),而且在根據後臺的狀態碼返回對應的方法。common方法接受一個參數params裏面除了axios須要的參數外還有successNotice(成功的時候是否顯示通知)、error(ajax失敗的時候調用的方法)、abnormal(ajax出現異常的是調用的方法)、success(請求成功的時候調用的方法)這些方法都會覆蓋子類的配置(參數配置優先)這樣咱們就完成了axios二次封裝和統一ajax異常處理

4.全局loading

在作全局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> 複製代碼

5.準備工做都作完了,咱們如今測試一下

新建network\api\imp\裏面新建一個api實現類UserApi.js繼承與Api.js並實現環繞beforeafter

<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')
})

複製代碼

最後的效果就是

6.裝飾器模式

到這一步vue-cli的改造基本上是完了,可是仍是缺了點什麼咱們能夠利用裝飾器對api的實現層就行進一步改造咱們先安裝如下裝飾的依賴並把babel-plugin-transform-decoratorsbabel-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的改造結束。

相關文章
相關標籤/搜索