前端复习——AJAX(二)

Ajax加强

XMLHTTPRequest的基本使用

XMLHTTPRequest(简称xhr)是浏览器提供的JavaScript对象,通过它,可以请求服务器上的数据资源。之前所学的jQuery中的Ajax函数,就是基于xhr对象封装出来的。
在这里插入图片描述

使用xhr发起GET请求

步骤:

  1. 创建xhr对象
  2. 调用xhr.open()函数
  3. 调用xhr.send()函数
  4. 监听xhr.onreadystatechage事件
//1.创建xhr对象
var ahr = new XMLHttpRequest()
//2.调用open函数,指定请求方式与URL地址
xhr.open('GET','http://www.liulongbin.top:3006/api/getbooks')
//3.调用end函数,发起Ajax请求
xhr.send()
//4.监听onreadystatechage事件
xhr.onreadystatechage = function() {
	//4.1监听xhr对象的请求状态readyState;与服务器响应的状态status
	if(xhr.readyState === 4 && xhr.state === 200) {
		//4.2打印服务器响应回来的数据
		console.log(xhr.responseText)
	}
}

了解xhr对象的readyState属性

XMLHttpRequest对象的readyState属性,用来表示当前Ajax请求所处的状态。每个Ajax请求必然处于一下状态中的一个:

状态描述
0UNSENTXMLHttpRequest对象已被创建,但尚未调用open方法。
1OPENEDopen()方法已被调用。
2HEADERS_RECEIVEDsend()方法已被调用,响应头也已经被接收。
3LOADING数据接收中,此时response属性中已经包含部分数据。
4DONEAjax请求完成,这意味着数据传输已经彻底完成失败

使用xhr发起带参数的GET请求

使用xhr对象发起带参数的GET请求时,只需在调用xhr.open期间,为URL地址指定参数即可:

//省略不必要的代码
xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks?id=1')
//...省略不必要的代码

这种在URL地址后面拼接的参数,叫做查询字符串

var xhr = new XMLHttpRequest()
xhr.open('GET','http://www.liulongbin.top:3006/api/getbooks?id=1')
xhr.send()
xhr.onreadystatechage = function() {
	if(xhr.readyState === 4 && xhr.state === 200) {
		console.log(xhr.responseText)
	}
}

查询字符串

1.什么是查询字符串

查询字符串定义:查询字符串(URL参数)是指在URL的末尾加上用于向服务器发送信息的字符串(变量)。

格式:将英文的放在URL的末尾,然后再加上参数=值,想加上多个参数的话,使用&符号进行分割。以这个形式,可以将想要发送给服务器的数据添加到URL中。

//不带参数的URL地址
http://www.liulongbin.top:3006/api/getbooks
//带一个参数的URL地址
http://www.liulongbin.top:3006/api/getbooks?id=1
//带两个参数的URL地址
http://www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记
2.Get请求携带参数的本质

无论使用$.ajax(),还是使用$.get(),又或者直接使用xhr对象发起GET请求,当需要携带参数的时候,本质上,都是直接将参数以查询字符串的形式,追加到URL地址的后面,发送到服务器的。

$.get('url', { name: 'zs', age: 20},function() {})
//等价于
$.get('url?name=zs&age=20', function() {})

$.ajax({ method: 'GET', url: 'url', data: {name: 'zs', age: 20}, success: function() {} })
//等价于
$.ajax({ method: 'GET', url: 'url?name=zs&age=20',success: function() {} })
/*$.get('http://www.liulongbin.top:3006/api/getbooks', {id: 1,bookname: '西游记'}, function(res) {
	console.log(res)
})*/
$.ajax({ 
	method: 'GET',
	url: 'http://www.liulongbin.top:3006/api/getbooks',
	data: {id: 1,bookname: '西游记'},
	success: function(res) {
		console.log(res)
	}
})

URL编码与解码

URL地址中,只允许出现英文相关的字母、标点符号、数字,因此,在URL地址中不允许出现中文字符。

如果URL中需要包含中文这样的字符,则必须对中文字符进行编码(转义)。

URL编码原则:使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示哪些不安全的字符。

URL编码原则的通俗理解:使用英文字符去表示非英文字符

http://www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记
//经过URL编码之后,URL地址变成了如下格式
http://www.liulongbin.top:3006/api/getbooks?id=1&bookname=%E8%A5%BF%E6%B8%B8%E8%AE%B0
如何对URL进行编码与解码

浏览器提供了URL编码与解码的API,分别是:

  • encodeURL()编码的函数
  • decodeURL()解码的函数
encodeURL('程序员')
//输出字符串 %E7%A8%8B%E5%BA%8F%E5%91%98
decodeURL('%E7%A8%8B%E5%BA%8F%E5%91%98')
//输出字符串 程序员

由于浏览器会自动对URL地址进行编码操作,因此,大多数情况下,程序员不需要关心URL地址的编码与解码操作。

使用xhr发起POST请求

步骤:

  1. 创建xhr对象
  2. 调用xhr.open()函数
  3. 设置Content-Type属性(固定写法)
  4. 调用xhr.send()函数,同时指定要发送的数据
  5. 监听xhr.onreadystatechage事件
//1.创建xhr对象
var xhr = new XMLHttpRequest()
//2.调用open函数
xhr.open('POST','http://www.liulongbin.top:3006/api/addbooks')
//3.设置Content-Type属性(固定写法)
xhr.setRequestHeader('Content-Type', 'applicationn/x-www-form-urlencoded')
//4.调用end函数,同时将数据以查询字符串的形式,提交给服务器
xhr.send('bookname=水浒传&author=&施耐庵publisher=天津图书出版社')
//5.监听onreadystatechage事件
xhr.onreadystatechage = function() {
	if(xhr.readyState === 4 && xhr.state === 200) {
		console.log(xhr.responseText)
	}
}

数据交换格式

数据交换格式,就是服务器端客户端之间进行数据传输与交换的格式

前端领域,经常提及的两种数据交换格式分别是XMLJSON。其中XML用的非常少,所以,我们重点要学习的数据交换格式就是JSON。
在这里插入图片描述

XML

XML全称是(EXtensible Markup Language),即可扩展标记语言,因此,XML和HTML类似,也是一种标记语言。
在这里插入图片描述

XML和HTML的区别

XML和HTML虽然都是标记语言,但是,它们两者之间没有任何的关系

  • HTML被设计用来描述网页上的内容,是网页内容的载体
  • XML被设计用来传输和存储数据,是数据的载体
    在这里插入图片描述
XML的缺点

在这里插入图片描述

JSON

概念:JSON的英文全称是JavaScript Object Notation,即“JavaScript对象表示法”。简单来讲,JSON就是JavaScript对象和数组的字符串表示法,它使用文本表示一个JS对象或数组的信息,因此,JSON的本质是字符串

作用:JSON是一种轻量级的文本数据交换格式,在作用上类似于XML,专门用于存储和传输数据,但是JSON比XML更小、更快、更易解析

现状:JSON是在2001年开始被推广和使用的数据格式,现今为止,JSON已经成为了主流的数据交换格式

JSON的两种结构

JSON就是用字符串来表示JavaScript的对象和数组,所以,JSON中包含对象和数组两种结构,通过这两种结构的相互嵌套,可以表示各种复杂的数据结构。

对象结构:对象结构在JSON中表示为{}括起来的内容,数据结构为{key: value,key: value, ...}的键值对结构,其中,key必须是使用英文的双引号包裹的字符串,value的数据类型可以是数字、字符串、布尔值、nul、数组、对象6中类型。
在这里插入图片描述
数组结构:数组结构在JSON中表示为[]括起来的内容。数据结构为["java","javaecript",30,true...]。数组中数据的1类型可以是数字、字符串、布尔值、null、数组、对象6种类型。
在这里插入图片描述

JSON语法的注意事项
  1. 属性名必须使用双引号包裹
  2. 字符串类型的值必须使用双引号包裹
  3. JSON中不允许使用单引号表示字符串
  4. JSON中不能写注释
  5. JSON的最外层必须是对象或数组格式
  6. 不能使用undefined或函数作为JSON的值

JSON的作用:在计算机与网络之间存储和传输数据
JSON的本质:用字符串来表示JavaScript对象数据或数组数据

JSON和JS对象的关系

JSON是JS对象的字符串表示法,它使用文本表示一个JS对象的信息,本质是一个字符串。例如:

//这是一个对象
var obj = {a: 'hello', b: 'world'}
//这是一个JSON字符串,本质是一个字符串
var jsson = '{"a": "hello", "b": "world"}'
JSON和JS对象的互转

要实现从JSON字符串转换为JS对象,使用JSON.parse()方法:

var obj = JSON.parse('{"a": "hello", "b": "world"}')
//结果是 {a: 'hello', b: 'world'}

要实现从JS对象转换为JSON字符串,使用JSON.stringify()方法:

var json = JSON.stringify({a: 'hello', b: 'world'})
//结果是 '{"a": "hello", "b": "world"}'

在这里插入图片描述
在这里插入图片描述

序列和反序列化

数据对象转换为字符串的过程,叫做序列化,例如:调用JSON.stringify()函数的操作,叫做JSON序列化。

字符串转换为数据对象的过程,叫做反序列化,例如:调用JSON.parse()函数的操作,叫做JSON反序列化。

封装自己的Ajax函数

在这里插入图片描述

定义options参数选项

itheima()函数是我们自定义的AJAX函数,它接收一个配置对象作为参数,配置对象中可以配置如下属性:

  • method:请求类型
  • url:请求的URL地址
  • data:请求携带的数据
  • success:请求成功之后的回调函数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
<script>
	function resolveData(data) {
		var arr = []
		for(var k in data) {
			var str = k + '=' + data[k]
			arr.push(str)
		}
		return arr.join('&')
	}
	
	//var res = resoleData({ name: 'zs', age: 20 })
	//console.log(res)
	
	function itheima(options) {
		var xhr = new XMLHttpRequest()
		
		//把外界传递过来的参数对象,转换为查询字符串
		var qs = esolveData(option.data)
		if(options.method.toUpperCase() === 'GET') {
			//发起GET请求
			xhr.open(options.method, options.url + '?' + qs)
			xhr.send()
		}else if (options.method.toUpperCase() === 'POST'){
			//发起POST请求
			xhr.open(options.method, options.url)
			xhr.setRequestHesder('Content-Type', 'application/x-www-form-urlencoded')
			xhr.send(qs)
		}
		
		xhr.onreadystatechanges = function(){
			if(xhr.readystate === 4 && xhr.status === 200){
				var result = JSON.parse(xhr.responseText)
				option.success(result)
			}
		}
	}
</script>
<script>
	/*itheima({
		method: 'GET',
		url: 'http://www.liulongbin.top:3006/api/getbooks',
		data: {
			id: 1
		},success: function(res) {
			console.log(res)
		}
	})*/
	itheima({
		method: 'POST',
		url: 'http://www.liulongbin.top:3006/api/addbooks',
		data: {
			bookname: '水浒传',
			author: '施耐庵',
			publisher: '北京图书出版社'
		},success: function(res) {
			console.log(res)
		}
	})
</script>

GET:
在这里插入图片描述
POST:
在这里插入图片描述

XMLHTTPRequest Level2的新特性

旧版XMLHttpRequest的缺点:

  1. 只支持文本数据的传输,无法用来读取和上传文件
  2. 传送和接收数据时,没有进度信息,只能提示有没有完成

XMLHTTPRequest Level2的新功能:

  1. 可以设置HTTP请求的时限
  2. 可以使用FormData对象管理表单数据
  3. 可以上传文件
  4. 可以获得数据传输的进度信息

设置HTTP请求的时限

有时,Ajax操作很耗时,而且无法预知要花多少时间,如果网速很慢,用户可能要等很久,新版本XMLHTTPRequest对象,增加了timeout属性,可以设置HTTP请求的时限:

xhr.timeout = 3000
//单位毫秒

上面的语句,将最长等待时间设为3000毫秒,过了这个时限,就自动停止HTTP请求,与之配套的还有一个tomeout事件,用来指定回调函数。

xhr.ontimeout = function(event) {
	alert('请求超时!')
}
var xhr = new XMLHttpRequest()

//设置超时时间
xhr.timeout = 3000
//设置超时以后的处理函数
xhr.ontimeout = function () {
	console.log('请求超时了!')
}

xhr.open('GET','http://www.liulongbin.top:3006/api/getbooks')
xhr.send()
xhr.onreadystatechage = function() {
	if(xhr.readyState === 4 && xhr.status === 200) {
		console.log(xhr.responseText)
	}
}

FormData对象管理表单数据

Ajax操作往往用来提交表单数据。为了方便表单处理,HTML5新增了一个FormData对象,可以模拟表单操作:

//1.新建FormData对象
var fd = new FormData()
//2.为FormData添加表单项
fd.append('uname', 'zs')
fd.append('upwd', '123456')
//3.创建XHR对象
var xhr = new XMLHttpRequest()
//4.指定请求类型与URL地址
xhr.open('POST','http://www.liulongbin.top:3006/api/formdata')
//5.直接提交FormData对象,这与提交网页表单的效果,完全一样
xhr.send(fd)

xhr.onreadystatechage = function() {
	if(xhr.readyState === 4 && xhr.status === 200) {
		console.log(xhr.responseText)
	}
}

在这里插入图片描述

FormData对象也可以用来获取网页表单的值,实例代码如下:

<form>
	<input type="text" name="uname" autocomplete="off" />
	<input type="password" name="upwd" />
	<button type="submit">提交</button>
</form>
<script>
	//通过DOM操作,获取表单元素
	var form = document.querySelector('#form1')
	//监听表单元素的sunmit事件
	form.addEventListener('submit', function(e) {
		//阻止表单默认提交行为
		e.preventDefault()
		//根据form表单创建FormData对象,会自动将表单数据填充到FormData对象中
		var fd = new FormData(form)
		var xhr = new XMLHttpRequest()
		xhr.open('POST','http://www.liulongbin.top:3006/api/formdata')
		xhr.send(fd)
		xhr.onreadystatechage = function() {
			if(xhr.readyState === 4 && xhr.status === 200) {
				console.log(JSON.parse(xhr.responseText))
			}
		}
	})
</script>

上传文件

新版XMLHttpRequest对象,不仅可以发送文本信息,还可以上传文件。
实现步骤:

  1. 实现UI结果
  2. 验证是否选择了文件
  3. 向FormData中追加文件
  4. 使用xhr发起上传文件的请求
  5. 监听onreadystatechange事件
定义UI结构
<!-- 1.文件选择框 -->
<input typr="file" id="file1" />
<!-- 2.上传按钮 -->
<button id="btnUpload">上传文件</button>
<!-- 3.显示上传到服务器上的图片 -->
<img src="" alt="" id="img" width`="800" />
验证是否选择了文件
//1.获取上传文件的按钮
var btnUpload = document.querySelector('#btnUpload')
//2.为按钮添加click事件监听
btnUpload.addEventListener('click', function() {
	//3.获取到选择的文件列表
	var files = document.queryDelector('#file1').files
	if(files.length <= 0) {
		return alert('请选择要上传的文件!')
	}
	//...后序业务逻辑
})
向FormData中追加文件
//1.创建FormData对象
var fd = new FormData()
//2.向FormData中追加文件
fd.append('avatar',files[0])
使用xhr发起上传文件的请求
//1.创建xhr对象
var xhr = new XMLHttpRequest()
//2.调用open函数,指定请求类型与URL地址,其中,请求类型必须为POST
xhr.open('POST','http://www.liulongbin.top:3006/api/upload/avatar')
//3.发起请求
xhr.send(fd)
监听onreadystatechange事件
xhr.onreadystatechage = function() {
	if(xhr.readyState === 4 && xhr.status === 200) {
		var data = JSON.parse(xhr.responseText)
		if(data.status === 200) {//上传文件成功
			//将服务器返回的图片地址,设置为<img>标签的src属性
			document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + data.url
		} else {//上传文件失败
			console.log(data.message)
		}
		
	}
}

显示文件上传进度

新版本的XMLHTTPRequest对象中,可以通过监听xhr.upload.onprogress事件,来获取到文件的上传进度。语法格式如下:

//创建xhr对象
var shr = new XMLHttpRequest()
//监听xhr.upload的onprogress事件
xhr.upload.onprogress = function(e) {
	//e.lengthComputable是一个布尔值,表示当前上传的资源是否具有可计算的长度
	if (e.lengthComputable) {
		//e.loaded已传输的字节
		//e.total带传输的总字节
		var percentComplete = Math.cell((e.loaded / e.total)*100)
	}
}
监听上传进度的事件
<link rel="stylesheet" href="./lib/bootstrap.css" />
<div class="progress" style="width: 500px;margin: 15px 10px;">
	<div class="progress-bar progress-bar-striped active" style="width: 0%" id="percent">
		0%
	</div>
</div>
xhr.upload.onprogress = function(e){
	if(e.lengthComputable) {
		//1.计算出当前上传进度的百分比
		var percentComplete = Math.ceil((e.loaded / e.total)*100)
		$('#percent')
			//2.设置进度条的宽度
			.attr('style', 'width:' + percentComplete + '%')
			//3.显示当前的上传进度百分比
			.html(percentComplete + '%')
	}
}
监听上传完成的事件
xhr.upload.onload = function(){
		$('#percent')
			//移除上传中的类样式
			.removeClass()
			//添加上传完成的类样式
			.addClass('progress-bar progress-bar-success')
}

jQuery高级用法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

axios

Axios是专注于网络数据请求的库。

相比于原生XMLHttpRequest对象,axios简单易用

相比于jQuery,axuos更加轻量化,只专注于网络数据请求。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

跨域和JSONP

了解同源策略和跨域

同源和同源策略

同源:如果两个页面的协议域名端口都相同,则两个页面具有相同的源

例如,下表给出了相关于http://www.test.com/index.html页面的同源检测:

URL是否同源原因
http://www.test.com/other.html同源(协议、域名、端口相同)
https://www.test.com/about.html协议不同(httphttps
http://blog.test.com/movie.html域名不同(www.test.comblog.test.com
http://www.test.com:7001/index.html端口不同(默认的80端口与7001端口)
http://www.test.com:80/main.html同源(协议、域名、端口相同)

同源策略(英文全称Same origin policy):是浏览器提供的一个安全功能

MDN官方给定的概念:同源策略限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

通俗理解:浏览器规定,A网站的JavaScript,不允许和非同源的网站C之间进行资源的交互,例如:

  1. 无法读取非同源网页的Cookie、LocalStorage和IndexedDB
  2. 无法接触非同源网页的DOM
  3. 无法向非同源地址发送Ajax请求

跨域

同源指的是两个URL的协议、域名和端口一致,反之,则是跨域

出现跨域的根本原因:浏览器的同源策略不允许非同源的URL之间进行资源的交互。
在这里插入图片描述
注意:浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到。

现如今,实现跨域数据请求,最重要的两种解决方案,分别是JSONPCORS

JSONP:出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持GET请求,不支持POST请求。

CORS:出现的比较晚,它是W3C标准,属于跨域Ajax请求的根本解决方案,支持GET和POST请求。缺点是不兼容某些低版本的浏览器。

JSONP

JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。

由于浏览器同源策略的限制,网页中无法通过Ajax请求非同源的接口数据。但是<script>标签不受浏览器同源策略的影响,可以通过src属性,请求非同源的js脚本。

因此,JSONP的实现原理,就是通过<script>标签的src属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据。

function success(data){
	console.log('拿到了data数据:')
	console.log(data)
}
//确保你调用的是getdata.js里的success而不是abc或者xyz
<script src="./js/getdata.js?callback=success"></script>

success{{name: 'zs', age: 20}}

在这里插入图片描述
JSONP的缺点:

由于JSONP是通过<script>标签的src属性,来实现跨域数据获取的,所以,JSONP只支持GET数据请求,不支持POST请求。

注意:JSONP和Ajax之间没有任何关系,不能把JSONP请求数据的方式叫做Ajax,因为JSNOP没有用到XMLHTTPRequest这个对象。

jQuery中的JSONP

jQuery提供的$.ajax()函数,除了可以发起真正的Ajax数据请求之外,还能够发起JSONP数据请求,例如:

$.ajax({
	url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
	//如果要使用$.ajax()发起JSONP请求,必须指定datatype为jsonp
	dataType: 'jsonp',
	success: function(res) {
		console.log(res)
	}
})

默认情况下,使用jQuery发起JSONP请求,会自动携带一个callback=jQueryxxx的参数,jQueryxxx是随机生成的一个回调函数名称。

自定义参数及回调函数名称

在使用jQuery发起JSONP请求时,如果想要自定义JSONP的参数以及回调函数名称,可以通过如下两个参数来指定:

$.ajax({
	url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
	dataType: 'jsonp',
	//发送到服务端的参数名称、默认值为callback
	jsonp: 'callback',
	//自定义的回调函数名称、默认值为jQueryxxx格式
	jsonpCallback: 'abc',
	success: function(res) {
		console.log(res)
	}
})

jQuery中的JSONP的实现过程

jQuery中的JSONP,也是通过<script>标签的src属性实现跨域数据访问的,只不过,jQuery采用的是动态创建和移除<script>标签的方式,来发起JSONP数据请求。

  • 发起JSONP请求的时候,动态向<header>中append一个<script>标签
  • JSONP请求成功以后,动态向<header>中append一个<script>标签

案例-淘宝搜索

在这里插入图片描述
在这里插入图片描述

获取用户输入的搜索关键词

在这里插入图片描述

封装getSuggestList函数

在这里插入图片描述

渲染建议列表的UI结构

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

输入框的防抖

防抖策略(debounce)是当事件被触发后,延迟n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时
在这里插入图片描述
防抖的应用场景:用户在输入框中连续输入一串字符时,可以通过防抖策略,只在输入完后,才执行查询的请求,这样可以有效减少请求次数,节约请求资源。
在这里插入图片描述

缓存搜索的建议列表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

防抖和节流

节流策略(throrrle),顾名思义,可以减少一段时间内事件的触发频率。
在这里插入图片描述
节流的应用场景:

  1. 鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;
  2. 懒加载时要监听计算滚动条的位置,但不必每次滑动都触发,可以降低计算的频率,而不必去浪费CPU资源。

节流案例——鼠标跟随效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 节流阀为,表示可以执行下次操作不为空,表示不能执行下次操作
  • 当前操作执行完,必须将节流阀重置为空,表示可以执行下次操作了
  • 每次执行操作前,必须先判断节流阀是否为空
    在这里插入图片描述

总结防抖和节流的区别

  • 防抖:如果事件被频繁触发,防抖能保证只有最后一次触发生效,前面N多次的触发都会被忽略。
  • 节流:如果事件被频繁触发,节流能够减少事件触发的频率,因此,节流是有选择地执行一部分事件。

——To Be Continue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值