vuejs发送异步请求

问题

使用vue时如何发送请求?


答:ajax,不过vue不使用jquery,那么需要引入一些HTTP库工具,使用其中封装好的请求方法即可

superagent或者Axios(主流)
使用文档

https://www.kancloud.cn/yunye/axios/234845
https://www.jianshu.com/p/1432e0f29abd

首先要安装相应的工具
运行命令

npm install xxx -D

xxx替换为对应的组件名

然后在src下的config目录新建js文件
假如现在使用的是superagent
api.js

// 配置API接口地址
var root = 'https://cnodejs.org/api/v1';
// 引用superagent
var request = require('superagent');
// 自定义判断元素类型JS
function toType(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
// 参数过滤函数
function filter_null(o) {
  for (var key in o) {
    if (o[key] == null) {
      delete o[key]
    }
    if (toType(o[key]) == 'string') {
      o[key] = o[key].trim()
      if (o[key].length == 0) {
        delete o[key]
      }
    }
  }
  return o
}
/*
  接口处理函数
  这个函数每个项目都是不一样的,我现在调整的是适用于
  https://cnodejs.org/api/v1 的接口,如果是其他接口
  需要根据接口的参数进行调整。参考说明文档地址:
  https://cnodejs.org/topic/5378720ed6e2d16149fa16bd
*/
function _api_base(method, url, params, success, failure) {
  var r = request(method, url).type('text/plain')
  if (params) {
    params = filter_null(params);
    if (method === 'POST' || method === 'PUT') {
      if (toType(params) == 'object') {
        params = JSON.stringify(params);
      }
      r = r.send(params)
    } else if (method == 'GET' || method === 'DELETE') {
      r = r.query(params)
    }
  }
  r.end(function(err, res) {
    if (err) {
      alert('api error, HTTP CODE: ' + res.status);
      return;
    };
    if (res.body.success == true) {
      if (success) {
        success(res.body);
      }
    } else {
      if (failure) {
        failure(res.body);
      } else {
        alert('error: ' + JSON.stringify(res.body));
      }
    }
  });
};
// 返回在vue模板中的调用接口
export default {
  get: function(url, params, success, failure) {
    return _api_base('GET', root + '/' + url, params, success, failure)
  },
  post: function(url, params, success, failure) {
    return _api_base('POST', root + '/' + url, params, success, failure)
  },
  put: function(url, params, success, failure) {
    return _api_base('PUT', root + '/' + url, params, success, failure)
  },
  delete: function(url, params, success, failure) {
    return _api_base('DELETE', root + '/' + url, params, success, failure)
  },
}

这是原博作者封装好的请求方法,开头的api地址是我们项目的基础请求地址头
这里填的是cnodejs.org提供的api接口

使用时只需要

先在启动入口里全局配置好

import api from './config/api'
Vue.prototype.$api=api

这两行的意思大概就是把api.js文件注册到全局,然后可以通过 $api.方法名 调用该文件里的方法。


<script type="text/javascript">
    import Header from '../components/header'
    import Footer from '../components/footer'



    export default{
        components:{Header,Footer},
        data(){
            return{
                lists:[]
            }
        },
        created(){
            this.get_data()
        },
        methods:{
            get_data:function(params){
                var v=this
                if(!params) params={}
                    v.$api.get('topics',params,function(r){
                        v.lists=r.data;
                    })
            },
        },
    }
</script>

created是该页面创建时执行的方法,params是传入的参数,’topics’是请求的地址,r是成功执行时返回的数据


然后该页面的template块为

<template>
  <div>
    <Header></Header>
    <ul class="list">
      <li v-for="item in lists">
          <time v-text="$fortime.goodTime(item.create_at)"></time>
          <router-link :to="'/content/' + item.id">{{item.title}}</router-link>
      </li>
    </ul>
    <Footer></Footer>
  </div>
</template>

fortime是我注册的工具类,用来格式化时间
router-link相当于a标签,请求的地址是/content/加上item的id


接下来写content里的内容,这次使用axios工具
一样,先install,创建一个index.js写入

// 配置API接口地址
var root = '/api/v1'
// 引用axios
var axios = require('axios')
// 自定义判断元素类型JS
function toType (obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
// 参数过滤函数
function filterNull (o) {
  for (var key in o) {
    if (o[key] === null) {
      delete o[key]
    }
    if (toType(o[key]) === 'string') {
      o[key] = o[key].trim()
    } else if (toType(o[key]) === 'object') {
      o[key] = filterNull(o[key])
    } else if (toType(o[key]) === 'array') {
      o[key] = filterNull(o[key])
    }
  }
  return o
}
/*
  接口处理函数
  这个函数每个项目都是不一样的,我现在调整的是适用于
  https://cnodejs.org/api/v1 的接口,如果是其他接口
  需要根据接口的参数进行调整。参考说明文档地址:
  https://cnodejs.org/topic/5378720ed6e2d16149fa16bd
  主要是,不同的接口的成功标识和失败提示是不一致的。
  另外,不同的项目的处理方法也是不一致的,这里出错就是简单的alert
*/

function apiAxios (method, url, params, success, failure) {
  if (params) {
    params = filterNull(params)
  }
  axios({
    method: method,
    url: url,
    data: method === 'POST' || method === 'PUT' ? params : null,
    params: method === 'GET' || method === 'DELETE' ? params : null,
    baseURL: root,
    withCredentials: false
  })
  .then(function (res) {
    if (res.data.success === true) {
      if (success) {
        success(res.data)
      }
    } else {
      if (failure) {
        failure(res.data)
      } else {
        window.alert('error: ' + JSON.stringify(res.data))
      }
    }
  })
  .catch(function (err) {
    let res = err.response
    if (err) {
      window.alert('api error, HTTP CODE: ' + res.status)
    }
  })
}

// 返回在vue模板中的调用接口
export default {
  get: function (url, params, success, failure) {
    return apiAxios('GET', url, params, success, failure)
  },
  post: function (url, params, success, failure) {
    return apiAxios('POST', url, params, success, failure)
  },
  put: function (url, params, success, failure) {
    return apiAxios('PUT', url, params, success, failure)
  },
  delete: function (url, params, success, failure) {
    return apiAxios('DELETE', url, params, success, failure)
  }
}

!这里不直接用原api地址是因为cnode.js的接口处理的很好,解决了跨域的问题。实际项目中,很多接口不允许跨域请求。为了解决这个问题,采用webpack的代理的方式
根目录的config文件夹下打开index.js,找到proxyTable这一行,里面就是我们要填的代理地址
这里我们写的是

proxyTable: {
    '/api/v1/**':{
        target: 'https://cnodejs.org',
        secure: false,
        changeOrigin: false,

    }
},

然后在main.js里注册

import aapi from './config/index'
Vue.prototype.$axapi=aapi

然后使用

<template>
  <div>
    <myHeader></myHeader>
    <h2 v-text="dat.title"></h2>
    <p v-if=dat.author>作者:{{dat.author.loginname}}  发表于:{{$fortime.goodTime(dat.create_at)}}</p>
    <hr>
    <article v-html="dat.content"></article>
    <h3>网友回复:</h3>
    <ul>
      <li v-for="i in dat.replies">
        <p>评论者:{{i.author.loginname}}  评论于:{{$fortime.goodTime(i.create_at)}}</p>
        <article v-html="i.content"></article>
      </li>
    </ul>
    <myFooter></myFooter>
  </div>
</template>
<script>
import myHeader from '../components/header.vue'
import myFooter from '../components/footer.vue'
export default {
  components: { myHeader, myFooter },
  data () {
    return {
      id: this.$route.params.id,
      dat: {}
    }
  },
  created () {
    this.getData()
  },
  methods: {
    getData () {
      this.$axapi.get('topic/' + this.id, null, r => {
        this.dat = r.data
      })
    }
  }
}
</script>

这里出过一个错就是,老提示loginname not defined,后来发现是author还没加载就加载了loginname,在p标签里加上v-if后解决问题


运行结果


说明请求成功,具体项目代码在

https://github.com/HolyDogs/VueStructDemo