axios笔记总结

1.1. axios 是什么?

  1. 前端最流行的 ajax 请求库
  2. react/vue 官方都推荐使用 axios 发 ajax 请求
  3. 文档: https://github.com/axios/axios
    1.2. axios 特点
  4. 基于 xhr + promise 的异步 ajax 请求库
  5. 浏览器端/node 端都可以使用
  6. 支持请求/响应拦截器
  7. 支持请求取消
  8. 请求/响应数据转换
  9. 批量发送多个请求

如果你没有服务器 则课在本地 创建一个简单的服务,有利于效率提高
前置知识 promise ajax
学习axios之前需要启动服务 如果没有后台接口,直接使用下面方法 创建一个服务
json-serve
安装

npm install -g json-serve

创建一个db.json 文件

在这里插入图片描述

开启服务
在这里插入图片描述
学习axios
安装方式
项目中使用 npm install axios /yarn add axios
或者使用script标签
通过bootcdn.cn 网站搜索axios
第一个小案例
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="button" value="发送get请求">
  <input type="button" value="发送post请求">
  <input type="button" value="发送put请求">
  <input type="button" value="发送delete请求">

</body>
<script src="./node_modules/axios/dist/axios.js"></script>
<script>
  const btns = document.querySelectorAll("input");
  btns[0].onclick = () => {
    axios({
      // 请求类型
      method: 'get',
      // URL
      url: 'http://localhost:3000/posts',
    }).then((res) => {
      console.log(res)
    }).catch((err) => {
      console.log(err)
    })
  }
  // 添加数据
  btns[1].onclick = () => {
    axios({
      method: 'post',
      url: 'http://localhost:3000/posts',
      // 请求体
      data: {
        title: "发送数据给json-serve",
        author: "发送请求"
      }
    }).then((res) => {
      console.log(res)
    })
  }
  // 跟新数据
  btns[2].onclick = () => {
    axios({
      method: 'put',
      url: 'http://localhost:3000/posts/3',
      //  设置请求体
      data: {
        title: "我更改了title",
        author: "我更改了author"
      }
    }).then((res) => {
      console.log(res)
    })
  }
  // 删除数据
  btns[3].onclick = () => {
    axios({
      method: 'delete',
      url: 'http://localhost:3000/posts/3',
    }).then((res) => {
      console.log(res)
    })
  }

</script>

</html>

则可进行 对db.json 文件进行修改

请求响应结果

在这里插入图片描述

请求配置
这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。

{
	// url 是用于请求的服务器 URL
	url: '/user',
	// method 是创建请求时使用的方法
	method: 'get', // 默认是 get
	// baseURL 将自动加在 url 前面,除非 url 是一个绝对 URL。
	// 它可以通过设置一个 baseURL 便于为 axios 实例的方法传递相对 URL
	baseURL: 'https://some-domain.com/api/',
	// transformRequest 允许在向服务器发送前,修改请求数据
	// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
	// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
	transformRequest: [function (data) {
		// 对 data 进行任意转换处理
		return data; 
	}],
	// transformResponse 在传递给 then/catch 前,允许修改响应数据
	transformResponse: [function (data) {
		// 对 data 进行任意转换处理
		return data;
	}],
	// headers 是即将被发送的自定义请求头
	headers: {'X-Requested-With': 'XMLHttpRequest'},
	// params 是即将与请求一起发送的 URL 参数// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
	params: {
		ID: 12345
	},
	// paramsSerializer 是一个负责 params 序列化的函数
	// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
	paramsSerializer: function(params) {
		return Qs.stringify(params, {arrayFormat: 'brackets'})
	},
	// data 是作为请求主体被发送的数据
	// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
	// 在没有设置 transformRequest 时,必须是以下类型之一:
	// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
	// - 浏览器专属:FormData, File, Blob
	// - Node 专属: Stream
	data: {
		firstName: 'Fred'
	},
	// timeout 指定请求超时的毫秒数(0 表示无超时时间)
	// 如果请求话费了超过 timeout 的时间,请求将被中断
	timeout: 1000,
	// withCredentials 表示跨域请求时是否需要使用凭证
	withCredentials: false, // 默认的
	// adapter 允许自定义处理请求,以使测试更轻松
	// 返回一个 promise 并应用一个有效的响应 (查阅 response docs).
	adapter: function (config) {
		/* ... */
	},
	// auth 表示应该使用 HTTP 基础验证,并提供凭据
	// 这将设置一个 Authorization 头,覆写掉现有的任意使用 headers 设置的自定义 Authorization头
	auth: {
		username: 'janedoe',
		password: 's00pers3cret'
	},
	// responseType 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
	responseType: 'json', // 默认的
	// xsrfCookieName 是用作 xsrf token 的值的cookie的名称
	xsrfCookieName: 'XSRF-TOKEN', // default
	// xsrfHeaderName 是承载 xsrf token 的值的 HTTP 头的名称
	xsrfHeaderName: 'X-XSRF-TOKEN', // 默认的
	// onUploadProgress 允许为上传处理进度事件
	onUploadProgress: function (progressEvent) {
		// 对原生进度事件的处理
	},
	// onDownloadProgress 允许为下载处理进度事件
	onDownloadProgress: function (progressEvent) {
		// 对原生进度事件的处理
	},
	// maxContentLength 定义允许的响应内容的最大尺寸
	maxContentLength: 2000,
	// validateStatus 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 validateStatus 返回 true (或者设置为 null 或 undefined),promise 将被 resolve; 否则,promise 将被 rejecte
	validateStatus: function (status) {
		return status >= 200 && status < 300; // 默认的
	},
	// maxRedirects 定义在 node.js 中 follow 的最大重定向数目
	// 如果设置为0,将不会 follow 任何重定向
	maxRedirects: 5, // 默认的
	// httpAgent 和 httpsAgent 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
	// keepAlive 默认没有启用
	httpAgent: new http.Agent({ keepAlive: true }),
	httpsAgent: new https.Agent({ keepAlive: true }),
	// 'proxy' 定义代理服务器的主机名称和端口
	// auth 表示 HTTP 基础验证应当用于连接代理,并提供凭据
	// 这将会设置一个 Proxy-Authorization 头,覆写掉已有的通过使用 header 设置的自定义 Proxy-Authorization 头。
	proxy: {
		host: '127.0.0.1',
		port: 9000,
		auth: : {
		username: 'mikeymike',
		password: 'rapunz3l'
	}

	// cancelToken 指定用于取消请求的 cancel token
	// (查看后面的 Cancellation 这节了解更多)
	cancelToken: new CancelToken(function (cancel) {
	})
}

响应结构
某个请求的响应包含以下信息

{
	// data 由服务器提供的响应
	data: {},
	// status 来自服务器响应的 HTTP 状态码
	status: 200,
	// statusText 来自服务器响应的 HTTP 状态信息
	statusText: 'OK',
	// headers 服务器响应的头
	headers: {},
	// config 是为请求提供的配置信息
	config: {}
}

使用 then 时,你将接收下面这样的响应:

axios.get('/user/12345')
.then(function(response) {
	console.log(response.data);
	console.log(response.status);
	console.log(response.statusText);
	console.log(response.headers);
	console.log(response.config);
});

在使用 catch 时,或传递 rejection callback 作为 then 的第二个参数时,响应可以通过 error 对象可被使用,正如在错误处理这一节所讲。

配置的默认值/defaults
你可以指定将被用在各个请求的配置默认值

全局的 axios 默认值

axios.defaults.method='GET'
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.params={id:1}
axios.defaults.timeout=300

自定义实例默认值
// 创建实例时设置配置的默认值
var instance = axios.create({
	baseURL: 'https://api.example.com'
});
// 在实例已创建后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
创建实例对象发送请求
  const duanzi = axios.create({
    url: 'https://api.apiopen.top',

  })
  duanzi({
    url: '/getJoke'
  }).then(response => {
    console.log(response)
  })
  const onather = axios.create({
    baseURL: 'https://www.baiducom',
    timeout: 3000,
  })

拦截器

分为两类 : 请求拦截器 响应拦截器
请求拦截器:发送请求是 通过一些函数 来判断是否有问题如果有问题,则停止发送请求
响应拦截器:处理结果之前可以先对结果进行预处理

 // 修改config中的参数  config 配置对象   
    axios.interceptors.request.use(config => {
      // Do something before request is sent
      console.log('请求拦截器 成功 - 1号')
      return config;
    }, error => {
      // Do something with request error
      console.log('请求拦截器 失败 - 1号')
      return Promise.reject(error);
    });
    axios.interceptors.request.use(config => {
      // Do something before request is sent
      console.log('请求拦截器 成功 - 2号')
      return config;
    }, error => {
      // Do something with request error
      console.log('请求拦截器 失败 - 2号')
      return Promise.reject(error);
    });
    axios.interceptors.response.use(response => {
      // Do something before response is sent
      console.log('响应拦截器 成功 - 1号')
      return response;
    }, error => {
      // Do something with response error
      console.log('响应拦截器 失败 - 1号')
      return Promise.reject(error);
    });
    axios.interceptors.response.use(response => {
      // Do something before response is sent
      console.log('响应拦截器 成功 - 2号')
      return response;
    }, error => {
      // Do something with response error
      console.log('响应拦截器 失败 - 2号')
      return Promise.reject(error);
    });
    axios({
      method: 'post',
      url: 'http://localhost:3000/posts',
    }).then(response => {
      console.log("获取数据")
    }).catch(err => {
      console.log("数据获取失败")
    })

在这里插入图片描述

如果你想在稍后移除拦截器,可以这样:

var myInterceptor = axios.interceptors.request.use(function () {/.../});
axios.interceptors.request.eject(myInterceptor);

可以为自定义 axios 实例添加拦截器

var instance = axios.create();
instance.interceptors.request.use(function () {/.../});
请求取消
    // 声明区局变量
    let cancel = null;

    btns[0].onclick = () => {
      // 检测上一次请求是否已经完成
      if(cancel!=null){
        cancel()
      }

      axios({
        method: 'post',
        url: 'http://localhost:3000/posts',
        // 添加配置对象的属性
        cancelToken: new axios.CancelToken((c) => {
          // 将c的值赋值给cancel
          cancel = c
        })
      }).then(response => {
        console.log(response)
      })
    }
    btns[1].onclick = () => {
      cancel()
    }

源码分析

├── /dist/ # 项目输出目录
├── /lib/ # 项目源码目录
│ ├── /adapters/ # 定义请求的适配器 xhr、http
│ │ ├── http.js # 实现 http 适配器(包装 http 包)
│ │ └── xhr.js # 实现 xhr 适配器(包装 xhr 对象)
│ ├── /cancel/ # 定义取消功能
│ ├── /core/ # 一些核心功能
│ │ ├── Axios.js # axios 的核心主类
│ │ ├── dispatchRequest.js # 用来调用 http 请求适配器方法发送请求的函数
│ │ ├── InterceptorManager.js # 拦截器的管理器
│ │ └── settle.js # 根据 http 响应状态,改变 Promise 的状态
│ ├── /helpers/ # 一些辅助方法
│ ├── axios.js # 对外暴露接口
│ ├── defaults.js # axios 的默认配置
│ └── utils.js # 公用工具
├── package.json # 项目信息
├── index.d.ts # 配置 TypeScript 的声明文件
└── index.js # 入口文件

模拟ajax请求

 // 声明构造函数
    function Axios(config) { this.config = config }
    // 发送请求
    Axios.prototype.request = function (config) {
      // 发送请求
      // 创建一个proist对象
      let promise = Promise.resolve(config);
      // 声明一个数组
      let chains = [dispatchRequest, undefined]
      // 调用then 方法指定回调
      let result = promise.then(chains[0], chains[1])
      //  返回promise 的结果
      return result;
    }

    // dispatchRequest 函数
    function dispatchRequest(config) {
      // 调用适配器发送请求 
      return xhrAdapter(config).then(response => {
        // 响应的结果进行转换处理
        return response
      }, error => {
        throw error
      })

    }
    // adapter 适配器
    function xhrAdapter(config) {
      console.log('xhr')
      return new Promise((reslove, reject) => {
        // 发送 ajax请求
        let xhr = new XMLHttpRequest()
        // 初始化
        xhr.open(config.method, config.url)
        // 发送
        xhr.send()
        // 绑定事件
        xhr.onreadystatechange = function () {
          // 判读成功的条件
          if (xhr.readyState === 4) {
            // 判断成功的条件
            if (xhr.status >= 200 & xhr.status < 300) {
              // 成功的状态
              resolve({
                // 配置对象
                config: config,
                // 响应体
                data: xhr.response,
                // xhr请求对象
                request: xhr,
                // 响应状态码
                status: xhr.status,
                // 响应状态符
                statusText: xhr.statusText
              })
            } else {
              reject(new Error('失败状态码', xhr.statusText))
            }
          }
        }
      })
    }

    // 创建axios函数
    let axios = Axios.prototype.request.bind(null)
    axios({
      method: 'post',
      url: 'http://localhost:3000/posts',
    }).then(response => {
      console.log(response)
    })

拦截器

// 构造函数
    function Axios(config) {
      this.config = config
      this.interceptors = {
        request: new InterceptorManager(),
        response: new InterceptorManager()
      }
    }
    // 发送请求  难点与重点
    Axios.prototype.request = function (config) {
      // 创建一个promise对象
      let promise = Promise.resolve(config)
      // 创建数组
      let chains = [dispatchRequest, undefined]
      // 请求拦截器 将请求拦截器的回调 压入到 chains的前面
      this.interceptors.request.handlers.forEach(element => {
        chains.unshift(element.fulfilled.rejected);
      });
      // 响应拦截器
      this.interceptors.response.handlers.forEach(element => {
        chains.push(element.fulfilled, element.rejected)
      })
      // 遍历
      while (chains.length > 0) {
        promise = promise.then(chains.shift(), chains.shift())
      }
      return promise;
    }
    //  发送请求
    function dispatchRequest(config) {
      return new Promise((resolve, reject) => {
        //  返回一个promise队形
        resolve({
          status: 200,
          statusText: 'OK'
        })
      })
    }
    // 创建实例
    let context = new Axios({})
    // 创建context属性 config interceptors 添加函数对象身上
    Object.keys(context).forEach(key => {
      axios[key] = context[key]
    })
    // 拦截器管理构造函数
    function InterceptorManager() {
      this.handlers = []
    }
    InterceptorManager.prototype.use = function (fulfilled, rejected) {
      this.handlers.push({
        fulfilled,
        rejected
      })
    }

    //以下为功能测试代码
    // 设置请求拦截器  config 配置对象
    axios.interceptors.request.use(function one(config) {
      console.log('请求拦截器 成功 - 1号');
      return config;
    }, function one(error) {
      console.log('请求拦截器 失败 - 1号');
      return Promise.reject(error);
    });

    axios.interceptors.request.use(function two(config) {
      console.log('请求拦截器 成功 - 2号');
      return config;
    }, function two(error) {
      console.log('请求拦截器 失败 - 2号');
      return Promise.reject(error);
    });

    // 设置响应拦截器
    axios.interceptors.response.use(function (response) {
      console.log('响应拦截器 成功 1号');
      return response;
    }, function (error) {
      console.log('响应拦截器 失败 1号')
      return Promise.reject(error);
    });

    axios.interceptors.response.use(function (response) {
      console.log('响应拦截器 成功 2号')
      return response;
    }, function (error) {
      console.log('响应拦截器 失败 2号')
      return Promise.reject(error);
    });


    //发送请求
    axios({
      method: 'GET',
      url: 'http://localhost:3000/posts'
    }).then(response => {
      console.log(response);
    });

请求取消

 //构造函数
        function Axios(config){
            this.config = config;
        }
        //原型 request 方法
        Axios.prototype.request = function(config){
            return dispatchRequest(config);
        }
        //dispatchRequest 函数
        function dispatchRequest(config){
            return xhrAdapter(config);
        }
        //xhrAdapter
        function xhrAdapter(config){
            //发送 AJAX 请求
            return new Promise((resolve, reject) => {
                //实例化对象
                const xhr = new XMLHttpRequest();
                //初始化
                xhr.open(config.method, config.url);
                //发送
                xhr.send();
                //处理结果
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4){
                        //判断结果
                        if(xhr.status >= 200 && xhr.status < 300){
                            //设置为成功的状态
                            resolve({
                                status: xhr.status,
                                statusText: xhr.statusText
                            });
                        }else{
                            reject(new Error('请求失败'));
                        }
                    }
                }
                //关于取消请求的处理
                if(config.cancelToken){
                    //对 cancelToken 对象身上的 promise 对象指定成功的回调
                    config.cancelToken.promise.then(value => {
                        xhr.abort();
                        //将整体结果设置为失败
                        reject(new Error('请求已经被取消'))
                    });
                }
            })
        }

        //创建 axios 函数
        const context = new Axios({});
        const axios = Axios.prototype.request.bind(context);

        //CancelToken 构造函数
        function CancelToken(executor){
            //声明一个变量
            var resolvePromise;
            //为实例对象添加属性
            this.promise = new Promise((resolve) => {
                //将 resolve 赋值给 resolvePromise
                resolvePromise = resolve
            });
            //调用 executor 函数
            executor(function(){
                //执行 resolvePromise 函数
                resolvePromise();
            });
        }

        //获取按钮 以上为模拟实现的代码
        const btns = document.querySelectorAll('button');
        //2.声明全局变量
        let cancel = null;
        //发送请求
        btns[0].onclick = function(){
            //检测上一次的请求是否已经完成
            if(cancel !== null){
                //取消上一次的请求
                cancel();
            }

            //创建 cancelToken 的值
            let cancelToken = new CancelToken(function(c){
                cancel = c;
            });

            axios({
                method: 'GET',
                url: 'http://localhost:3000/posts',
                //1. 添加配置对象的属性
                cancelToken: cancelToken
            }).then(response => {
                console.log(response);
                //将 cancel 的值初始化
                cancel = null;
            })
        }

        //绑定第二个事件取消请求
        btns[1].onclick = function(){
            cancel();
        }

拦截器


    // 构造函数
    function Axios(config) {
      this.config = config
      this.interceptors = {
        request: new InterceptorManager(),
        response: new InterceptorManager()
      }
    }
    // 发送请求  难点与重点
    Axios.prototype.request = function (config) {
      // 创建一个promise对象
      let promise = Promise.resolve(config)
      // 创建数组
      let chains = [dispatchRequest, undefined]
      // 请求拦截器 将请求拦截器的回调 压入到 chains的前面
      this.interceptors.request.handlers.forEach(element => {
        chains.unshift(element.fulfilled.rejected);
      });
      // 响应拦截器
      this.interceptors.response.handlers.forEach(element => {
        chains.push(element.fulfilled, element.rejected)
      })
      // 遍历
      while (chains.length > 0) {
        promise = promise.then(chains.shift(), chains.shift())
      }
      return promise;
    }
    //  发送请求
    function dispatchRequest(config) {
      return new Promise((resolve, reject) => {
        //  返回一个promise队形
        resolve({
          status: 200,
          statusText: 'OK'
        })
      })
    }
    // 创建实例
    let context = new Axios({})
    // 创建axios
    let axios = Axios.prototype.request.bind(context)
    // 创建context属性 config interceptors 添加函数对象身上
    Object.keys(context).forEach(key => {
      axios[key] = context[key]
    })
    // 拦截器管理构造函数
    function InterceptorManager() {
      this.handlers = []
    }
    InterceptorManager.prototype.use = function (fulfilled, rejected) {
      this.handlers.push({
        fulfilled,
        rejected
      })
    }

    //以下为功能测试代码
    // 设置请求拦截器  config 配置对象
    axios.interceptors.request.use(function one(config) {
      console.log('请求拦截器 成功 - 1号');
      return config;
    }, function one(error) {
      console.log('请求拦截器 失败 - 1号');
      return Promise.reject(error);
    });

    axios.interceptors.request.use(function two(config) {
      console.log('请求拦截器 成功 - 2号');
      return config;
    }, function two(error) {
      console.log('请求拦截器 失败 - 2号');
      return Promise.reject(error);
    });

    // 设置响应拦截器
    axios.interceptors.response.use(function (response) {
      console.log('响应拦截器 成功 1号');
      return response;
    }, function (error) {
      console.log('响应拦截器 失败 1号')
      return Promise.reject(error);
    });

    axios.interceptors.response.use(function (response) {
      console.log('响应拦截器 成功 2号')
      return response;
    }, function (error) {
      console.log('响应拦截器 失败 2号')
      return Promise.reject(error);
    });


    //发送请求
    axios({
      method: 'GET',
      url: 'http://localhost:3000/posts/1'
    }).then(response => {
      console.log(response);
    });

请求取消

 let btns = document.querySelectorAll("input")
    // 构造函数
    function Axios(config) {
      this.config = config
    }
    // 原型request 方法
    Axios.prototype.request = function (config) {
      return dispatchRequest(config)
    }
    // dispatchRequest 函数
    function dispatchRequest(config) {
      return xhrAdapter(config)
    }
    // xhrAdapter
    function xhrAdapter(config) {
      // 发送ajax请求
      return new Promise((resolve, reject) => {
        // 实例化对象
        const xhr = new XMLHttpRequest()
        xhr.open(config.method, config.url);
        xhr.send();
        xhr.onreadystatechange = function () {
          if (xhr.status === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              resolve({
                status: xhr.status,
                statusText: xhr.statusText
              })
            } else {
              reject(new Error('请求失败'))
            }
          }
        }
        // 关于请求的处理
        if (config.CancelToken) {
          // 对cancelToken 对象上的promise对象指定成功的回调
          config.CancelToken.promise.then(value => {
            xhr.abort()
            reject(new Error("请求被取消"))
          })
        }
      })
    }
    // 创建axios
    const context = new Axios({})
    const axios = Axios.prototype.request.bind(context)
    function CancelToken(executor) {
      var resolvePromise;
      this.promise = new Promise((resolve) => {
        resolvePromise = resolve;
      })
      executor(function () {
        resolvePromise()
      })
    }
    let cancel = null;
    btns[0].onclick = function () {
      if (cancel !== null) {
        cancel()
      }
      let cancelToken = new CancelToken(function (c) {
        cancel = c
      })
      axios({
        method: 'post',
        url: 'http://localhost:3000/post',
      }).then(respinse => {
        console.log(response)
        cancel = null
      })
    }
    btns[1].onclick = () => {
      cancel();
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘴巴嘟嘟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值