# 为什么网络封装

如果直接使用第三方网络框架,当需要更换框架或者重构时,异常恶心

因此,需要进行一步封装

# 选择什么网络模块

## 选择一: 传统的Ajax是基于XMLHttpRequest(XHR)

为什么不用它呢?
非常好解释, 配置和调用方式等非常混乱.
<mark>编码起来看起来就非常蛋疼</mark>.
所以真实开发中很少直接使用, 而是使用jQuery-Ajax

## 选择二: 在前面的学习中, 我们经常会使用jQuery-Ajax

相对于传统的 Ajax 非常好用.
为什么不选择它呢?

  • 首先, 我们先明确一点: 在Vue的整个开发中都是不需要使用 jQuery 了.
  • 那么, 就意味着为了方便我们进行一个网络请求, 特意引用一个jQuery, 你觉得合理吗?
    jQuery的代码1w+行.
    Vue的代码才1w+行.
    <mark>完全没有必要为了用网络请求就引用这个重量级的框架</mark>.

## 选择三: 官方在Vue1.x的时候, 推出了Vue-resource .

Vue-resource 的体积相对于jQuery小很多.
另外Vue-resource是官方推出的.
为什么不选择它呢?

  • <mark>在 Vue2.0 退出后, Vue作者就在GitHub的Issues中说明了去掉vue-resource, 并且以后也不会再更新</mark>.

那么意味着以后vue-reource不再支持新的版本时, 也不会再继续更新和维护.
对以后的项目开发和维护都存在很大的隐患.

## 选择四: axios

在说明不再继续更新和维护vue-resource的同时,
<mark>作者还推荐了一个框架: axios 为什么不用它呢?</mark>
axios有非常多的优点, 并且用起来也非常方便.

稍后, 我们对他详细学习.

# 同源策略 sop

跨域访问问题:
根据 同源策略(SOP same origin policy) 我们的项目部署在 domain1.com 服务器上时, 是不能直接访问 domain2.com 服务器上的资料的.

同源策略以及绕过此限制的方法

解决方案:
这个时候, 我们利用 <script> 标签的 src 帮助我们去服务器请求到数据, 将数据当做一个 javascript 的函数来执行, 并且执行的过程中传入我们需要的 json .

# jsonp

首先,要了解 jsonp (JSON with padding) - “填充式的JSON”

这篇文章讲 jsonp 透彻:《jsonp原理详解——终于搞清楚jsonp是啥了》

在前端开发中, 我们一种常见的网络请求方式就是 JSONP
使用 JSONP 最主要的原因往往是为了 <mark>解决跨域访问的问题</mark>.

## JSONP 的原理是什么呢?

<mark>JSONP的核心在于通过 <script> 标签的 src 来帮助我们请求数据.</mark>

<mark>所以, 封装 jsonp 的核心就在于我们监听 window 上的 jsonp 进行回调时的名称.</mark>

## JSONP如何封装呢?

我们一起自己来封装一个处理JSONP的代码吧.

# axios 静态

Github - https://github.com/axios/axios

功能特点:

  • 在浏览器中发送 XMLHttpRequests 请求
  • node.js 中发送 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 等等

个人理解:
axios == ajax i/o system

## axiox请求方式

支持多种请求方式:

  • axios(config)
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

如何发送请求呢?
我们看一下下边的案例

## 发送get请求演示

https://github.com/axios/axios#installing

安装

npm i axios --save
// axios 跨域
import axios from 'axios'

// /home/multidata
// /home/data?type=sell&page=3
axios({
  url: 'http://localhost:8001/home/multidata',
  method: 'get'
}).then(value => {
  console.log('http://localhost:8001/home/multidata', value)
})

// String[] types = { "phone", "book", "computer", "clothing", "daily-supply", "adult", "CD", "virtual" };
axios.get('http://localhost:8001/order/data', {
  // 专门针对get请求的参数拼接
  params: {
    type: 'book',
    page: 2
  }
}).then(value => {
  console.log('http://localhost:8001/order/data', value)
})


## axios 并发请求:axios.all([axios()…])


axios
  .all([
    axios({
      url: 'http://localhost:8001/home/multidata',
      method: 'get'
    }),
    axios.get('http://localhost:8001/order/data', {
      // 专门针对get请求的参数拼接
      params: {
        type: 'book',
        page: 3
      }
    })
  ])
  .then(value => {
    console.log('value', value)
    return Promise.resolve(value)
  })
  .then(
    axios.spread((res1, res2) => {
      console.log('res1', res1)
      console.log('res2', res2)
    })
  )

## 全局配置

Github - https://github.com/axios/axios#config-defaults

在上面的示例中, 我们的BaseURL是固定的
事实上, 在开发中可能很多参数都是固定的.
这个时候我们可以进行一些抽取, 也可以利用axiox的全局配置

axios.defaults.baseURL = '123.207.32.32:8000'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

## 常见配置

https://github.com/axios/axios#request-config

# 分布式概念

生产环境,一般使用 nginx 反向***,形成 分布式 的后台服务。

减轻服务器压力,增加灵活性

# axios 实例

上面说,通过反向***访问分布式服务,我们不需要在多个域中跨域访问。
(但是,下面我们假定没有 反向***,我们需要在多个环境下获取服务)

这样的话,只有全局的静态的 axios 显然是不行的。

我们需要 axios 实例,为我们准备环境。

// 创建对应的 axios 的实例1
const instance1 = axios.create({
	baseURL: 'http://222.111.33.33:8000' , 
	timeout: 5000
})

// 使用实例1
instance1({
	rul: '/home/multidata' 
}).then(res => {
	console.log()res;
})

// 使用实例2
instance1({
	rul: '/home/multidata' 
}).then(res => {
	console.log()res;
})

////////////////////////////////////////////////////////////

// 创建对应的 axios 的实例1
const instance2 = axios.create({
	baseURL: 'http://222.111.33.33:8001' , 
	timeout: 1000
})

// 使用实例1
instance1({
	rul: '/home/multidata' 
}).then(res => {
	console.log()res;
})

// 使用实例2
instance1({
	rul: '/home/multidata' 
}).then(res => {
	console.log()res;
})

# 封装 request 模块

创建目录 @/network 专门存放网络模块
创建 @/network/request.js 写网络请求代码

import axios from 'axios'

// 封装 request 模块
export function request (config) {
  // 1. 创建 axios 的实例
  const instance = axios.create({
    baseURL: 'http://localhost:8001',
    timeout: 5000
  })

  // 发送真正的网络请求
  return instance(config)
}

然后用自己封装好的组件调用

代码下载:https://github.com/LawssssCat/vue-springboot-cros_jsonp

# ***

axios 提供了***,用于我们在发送每次请求或者得到相应后,进行对应的处理。

如何使用***呢?

中文 api 文档 - http://www.axios-js.com/docs/#Interceptors

## 请求拦截

在前面的代码中添加 成功拦截

import axios from 'axios'

// 封装 request 模块
export function request (config) {
  // 1. 创建 axios 的实例
  const instance = axios.create({
    baseURL: 'http://localhost:8001',
    timeout: 5000
  })

  // axios 的***
  instance.interceptors.request.use(
    config=> {
      console.log('interceptor success', config)
      // return config
    },
    err => {
      console.log('interceptor err', err)
    }
  )

  // 发送真正的网络请求
  return instance(config)
}

拦截成功请求!

<mark>但是有报错</mark>,

TypeError: Cannot read property 'cancelToken' of undefined
    at throwIfCancellationRequested (dispatchRequest.js?c4bb:12)
    at dispatchRequest (dispatchRequest.js?c4bb:24)

原因:我们拦截的 value 配置信息 config

我们需要把请求的配置向下传递,才能正确发送请求

## 使用情况

由上面请求可见,***是真正可以进行请求拦截的

  • 校验:<mark>我们可以对 config 中一些不符合服务器的要求给进行拦截</mark>

  • 增强:<mark>其次,我们可以通过***,在发送请求前做一些操作</mark>
    如:
    在请求前,给界面添加 进度条
    然后在收到响应,进行处理前,去掉进度条

  • 携带信息
    比如:给请求携带 token 信息

## 响应拦截

响应拦截 同理

import axios from 'axios'

// 封装 request 模块
export function request (config) {
  // 1. 创建 axios 的实例
  const instance = axios.create({
    baseURL: 'http://localhost:8001',
    timeout: 5000
  })

  // axios 的***
  instance.interceptors.request.use(
    config => { // config 是 请求的配置信息
      console.log('request interceptor success', config)
      return config
    },
    err => {
      console.log('request interceptor err', err)
    }
  )
  instance.interceptors.response.use(response => {
    console.log('response interceptor success', response)
    return response
  }, err => {
    console.log('response interceptor err', err)
  })

  // 发送真正的网络请求
  return instance(config)
}