Ajax 学习之路

初识Ajax

  1. Ajax 的作用

获取服务器的数据

  1. Ajax 的效果

在不刷新整个页面的情况,通过一个url地址获取服务器的数据,然后进行页面的局部刷新

  1. 一些熟悉的场景
  • 评论加载效果

  • 用户名验证

局部 异步 刷新

Ajax 的全称: Asyncchronous Javascript And XML , 就是使用JS代码获取服务器数据

基础知识铺垫

服务器与客户端

A. 概念层面:
a. 服务器:能够提供某种服务的电脑
b. 客户端:想使用服务器所提供服务的电脑

B. 硬件层面:
a. 服务器:由于要给千千万万个客户端提供服务,因此一般来说,服务器的硬件配置更高一些
b. 客户端:个人电脑、手机、平板等都可以称为客户端

注意:服务器与客户端在硬件层面上并没有明显的划分,配置很差的个人电脑依然可以当作服务器使用,只不过速度慢一点而已。

C. 服务器能提供什么服务?

一些我们日常使用的功能,其实都是服务器所提供的服务。比如网页服务、邮箱服务、文件上传与下载服务、聊天服务等等…

D. 服务器软件

服务器能提供服务由于在服务器操作系统上安装了很多软件,由于这些软件对外提供服务,比如:

HTTP网页服务:Apache、Tomcat、IIS等
文件上传下载服务:VsFtp等
邮箱服务:SendMail等
数据存储服务:MySql、Oracle等

小结:服务器就是提供服务的,客户端就是使用服务器所提供的服务。

网络相关概念

a. IP地址

通过ip地址就能找到特定的服务器,比如百度服务器的IP地址为:123.125.114.144

b. 域名

域名就相当于地名一样,方便人们查找到服务器
比如说:www.baidu.com
查看本机ip : ipconfig

c. DNS 域名解析服务器

DNS 提供域名与ip地址的映射关系

访问服务器的流程:本机hosts文件 ——> DNS 服务器 ——> 服务器
本机host 文件的路径为:C\Windows\System32\drivers\etc\HOSTS

在这里插入图片描述

d. 端口

前面我们说过,服务器就是提供服务的。ip地址是用来查找某一台服务器的。域名是方便人们记忆的。DNS维护着域名和ip地址的映射关系。

所以通过域名是可以找到一台服务器的
但是一台服务器可能提供多种服务,我们找到这台服务器时,究竟是想使用这台服务器的什么服务呢?这就使用端口号来区分。

其实我们每次访问网页,最完整的写法应该是:http://www.baidu.com:80 80这个端口比较特殊,可以省略不写

小结:ip地址是用来查找某一台服务器的。域名是方便人们记忆的。DNS维护着域名和ip地址的映射关系。端口是用来区分一台服务器上提供的不同服务的。

通信协议

通信协议就是事先规定好的规则。(机器与机器之间的交流)

常见的协议:

HTTP、HTTPS、超文本传输协议
FTP文件传输协议
SMTP简单邮件传输协议

在HTTP协议中,需要大致了解的是:请求头、响应头、请求体、响应体。

Wamp的安装

为什么要安装Wamp

将我们写的 html 界面以服务的方式分享给别人看,否则别人的电脑是无法看到我们所写的界面的

Wamp是什么

Wamp 指的是:Windows、appache、mysql、php 几个服务器软件的缩写,类似的还有Lamp:linux、apache、mysql、php

Wamp的安装与简单配置

a. 配置访问权限,默认情况下,apache提供的网页服务只允许Localhost和127.0.0.1 进行访问,我们需要对配置进行修改。配置文件位于 D:\Application program\wamp\bin\apache\Apache2.4.4\conf 将268行的Deny from all 改为Allow from all

b. 网站根路径的配置,默认情况下,网站的根路径为 C:\wamp\www 此目录可以修改。将 D:\Application program\wamp\bin\apache\Apache2.4.4\conf 中的239行的DocumentRoot 进行修改即可

比如在D盘下创建一个myweb文件夹,添加一个test.html 测试界面,然后在另一台电脑的浏览器地址栏输入 http://192.168.213.1/test.html (这里的ip地址是利用 ipconfig 命令查询到的本地计算机的ip) 运行显示出测试界面 表示运行没问题。

在这里插入图片描述

虚拟主机的配置

如果一台服务器想提供多个站点,那么就需要对虚拟主机进行配置。

更改配置文件时,首先备份一份。找到C:\Windows\System32\drivers\etc 里的hosts文件,增加IP地址的映射
在这里插入图片描述

然后在 D:\Application program\wamp\bin\apache\Apache2.4.4\conf 下的 httpd.conf 文件里 去掉这行注释符号

在这里插入图片描述

最后再对 D:\Application program\wamp\bin\apache\Apache2.4.4\conf\extra 下的 httpd-vhosts.conf 文件进行修改(增加需要的虚拟站点即可)。

在这里插入图片描述

PHP 基本语法

  1. 网站的分类:静态网站和动态网站

    a.静态网站
    全部由HTML代码格式页面组成的网站,没有数据库的支持,在网站制作和维护方面工作量较大
    b.动态网站
    动态网站并不是指具有动画功能的网站,而是指网站内容可根据不同情况动态变更的网站。一般情况下动态网站通过数据库进行架构。一般动态网站体现在网页一般是以 asp,jsp,php,aspx等结尾,动态网页以数据库为基础,可以大大降低网站维护的工作量,维护方便。

  2. PHP 语法的基本结构

    a.所有的PHP代码都要写到 <?php...?> 里面
    b.PHP文件可以和HTML相互结合进行使用
    c.PHP文件的默认默认文件扩展名是".php"
    d.PHP代码必须在服务器上执行

  3. echo 的使用
    echo 的作用就是向页面中输入字符串
    print_r 输出复杂类型
    var_dump 输出复杂类型

  4. 变量的声明和变量的使用
    无论是变量的声明还是变量的使用都需要使用$符号

  5. 字符串的拼接
    字符串的拼接使用 . 进行连接

  6. php 的执行原理

浏览器是不识别PHP代码的,PHP代码必须在服务器中执行,双击打开 php文件是达不到效果的。

在这里插入图片描述

  1. 数组相关

a.一维数组

数组的定义:

    $arr = array("zhangsan","lisi","wangwu");

    echo $arr[2]; // 只能输出字符串

    print_r($arr); // 可以输出复杂类型
    var_dump($arr); // 可以输出复杂类型

    // echo json_encode($arr); // 将数组转化为json格式的字符串
    // 可以拆分成以下这样
    $result = json_encode($arr);
    echo $result;

PHP当中数组的特点:下标可以自定义

    $arr = array("zhangsan","name1"=>"lisi","wangwu");
    // 可以对数组的下标索引自定义
    var_dump($arr);

二维数组:

    // 二维数组
    $arr = array();
    $arr["zhangsan"] = array("age"=>19,"sex"=>"male","height"=>"180");
    $arr["lisi"] = array("age"=>18,"sex"=>"female","height"=>"160");
    $arr["wangwu"] = array("age"=>17,"sex"=>"male","height"=>"190");

    var_dump($arr);

    $result = json_encode($arr);
    echo $result;

在浏览器中显示如下(json格式,后续会经常用到):

在这里插入图片描述

数组遍历:

    // 数组遍历
    // 1. 普通for循环
    $arr1 = array("zhangsan","wangwu","lisi");
    for($i=0;$i<count($arr1);$i++){
        $arr1[$i];
        $temp = $arr1[$i];
        echo $temp . "<br>";
    }

    // 2. foreach,这种方式更常用
    foreach($arr as $key => $value) {
        echo $key . ">>>" . $value . "<br>";
    }
  1. PHP中的函数

a. json_encode php中将数组转化为json格式的字符串
b. var_dump 输出复杂的数据类型
c. print_r 输出复杂的数据类型
d. count 得到数组的长度

  1. 预定义变量

A. 请求类型

请求有时候是需要携带参数的,用来标识特定的要求,根据参数携带位置的不同可以简单的把请求分为 Get请求 和 Post请求
a. Get请求:参数在URL后面,多个参数用&进行连接
b. Post请求:参数在请求体中

在这里插入图片描述

B. 获取请求参数的值

a. $_GET[]
b. $_Post[]

AJAX 的使用

Ajax 简单来说,就是一个异步的javascript 请求,用来获取后台服务端的数据,而并不是整个界面进行跳转。

在原生 JS 中来实现Ajax主要的类就是XMLHttpRequest,它的使用一般有四个步骤:

  1. 创建XMLHttpRequest 对象

  2. 准备发送网络请求

  3. 开始发送网络请求

  4. 指定回调函数

下面通过代码来实现这四个步骤:

    window.onload = function(){
        var btn = document.getElementById("btn");
        btn.onclick = function(){
            var username = document.getElementById("username").value;
            // console.log(username);
            // 使用js代码进行checkUsername.php这个文件的访问,将username传递给这个文件

            var xhr = new XMLHttpRequest();
            xhr.open("get","checkUsername.php?username=" + username,true);
            xhr.send(null);
            xhr.onreadystatechange = function(){
                var result = xhr.responseText;
                // console.log(result);
                document.getElementById("result").innerText = result;
            };
        };
    }

html代码:

<body>
    <h1>注册界面</h1>
    <form action="register.php" method="post">
        用户名:<input type="text" name="username" id="username">
        <input type="button" value="验证用户名" id="btn">
        <span id="result"></span>
        <br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="提交">
    </form>
</body>

chechUsername.php 文件代码:

    $uname = $_GET["username"];

    // 按道理来说,这里应该是要查询数据库 此处简单模拟
    if($uname == "zhangsan") {
        echo "username exists";
    } else {
        echo "username ok";
    }

下面对 post请求 作详细讲解

    window.onload = function(){
        var btn = document.getElementById("btn");
        btn.onclick = function(){
            var username = document.getElementById("username").value;
            // console.log(username);
            // 使用js代码进行checkUsername.php这个文件的访问,将username传递给这个文件

            // 1. 创建XMLHttpRequest这个对象
            var xhr = null;
            if(window.XMLHttpRequest) {
                xhr = new XMLHttpRequest();
                } else {
                // IE6浏览器 这边是针对ie6做的兼容处理
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
            // 2. 准备发送
            // xhr.open("get","checkUsername.php?username=" + username,true); // 这里true代表异步(不写默认异步)
            xhr.open("post","checkUsername.php",true);
            // 3. 执行发送
            var param = "username=" + username;
            // 对于post请求来说的话,我们的参数应该放到请求体中
            // 设置xhr的请求头信息,这个步骤仅仅是针对于post请求才有的
            xhr.setRequestHeader("Content-type","application/x-www-form-urlencode");
            xhr.send(param);

            // 4. 设置回调函数
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    if(xhr.status == 200) {
                        // 得到数据
                        // xhr.responseXML
                        var result = xhr.responseText;
                        console.log(result);
                        document.getElementById("result").innerText = result;
                    }
                }  
            };
        };
    };

在这里插入图片描述

html 页面代码:

<body>
    <h1>注册界面</h1>
    <form action="register.php" method="post">
        用户名:<input type="text" name="username" id="username">
        <input type="button" value="验证用户名" id="btn">
        <span id="result"></span>
        <br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="提交">
    </form>
</body>

chechUsername.php 文件代码:

    $uname = $_POST["username"];

    // 按道理来说,这里应该是要查询数据库 此处简单模拟
    if($uname == "zhangsan") {
        echo "username exists";
    } else {
        echo "username ok";
    }

在用户名输入框输入 zhangsan

在这里插入图片描述

注册界面案例讲解

当前端界面需要从服务器获取数据的时候,其实就只要访问一个url地址,制定特定的参数即可。这个url地址所对应的jsp也好,php也好 其实服务器开发人员已经开发好了。服务器开发人员开发好相关的接口之后,会提供一份接口文档,在文档中会详细的说明你要获取什么数据的时候,访问什么地址,传入什么参数等等。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

以下是注册界面案例完整代码展示

<title>注册界面</title>
    <script>
        window.onload = function() {

            var username = document.querySelector("#username");
            var email = document.querySelector("#email");
            var phone = document.querySelector("#phone");

            username.onblur = function() {
                var usernameValue = username.value;
                // 将 usernameValue 提交给服务器,有服务器进行唯一性的校验
                // 1. 创建对象 兼容处理
                var xhr = null;
                if(window.XMLHttpRequest) {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
                // 2. 准备发送
                xhr.open("get","./server/checkUsername.php?uname=" + usernameValue,true);
                // 3. 执行发送
                xhr.send(null);
                // 4. 指定回调函数
                xhr.onreadystatechange = function() {
                    if(xhr.readyState == 4) {
                        if(xhr.status == 200) {
                            var result = xhr.responseText;
                            // console.log(result);
                            var username_result = document.querySelector("#username_result");
                            if(result == "ok") {
                                username_result.innerText = "用户名可用";
                            } else {
                                username_result.innerText = "用户名已被注册";
                            }
                        }
                    }
                }

            }
            
            email.onblur = function() {
                var emailValue = email.value;
                var xhr = null;
                if(window.XMLHttpRequest) {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
                var param = "e=" + emailValue;
                xhr.open("post","./server/checkEmail.php",true);
                // post 方法需要设置请求头 关于请求头里的内容不用强行记忆
                xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
                xhr.send(param);

                xhr.onreadystatechange = function() {
                    if(xhr.readyState == 4) {
                        if(xhr.status == 200) {
                            var result = xhr.responseText;
                            var email_result = document.querySelector("#email_result");
                            if(result == 0) {
                                // 邮箱可用
                                email_result.innerText = "邮箱可用";
                            } else {
                                // 邮箱不可用
                                email_result.innerText = "邮箱不可用";
                            }
                        }
                    }
                }
            };

            phone.onblur = function() {
                var phoneValue = phone.value;
                // 1 2 3 4
                var xhr = null;
                if(window.XMLHttpRequest) {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
                
                xhr.open("post","./server/checkPhone.php",true);
                var params = "phonenumber=" +phoneValue;
                xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
                xhr.send(params);

                xhr.onreadystatechange = function() {
                    if(xhr.readyState == 4){
                        if(xhr.status == 200) {
                            // xhr.responseText就是一个字符串
                            var result = xhr.responseText;

                            // 希望将result这样一个字符串转化为对象,方便我们获取里面的一些值
                            result = JSON.parse(result); // 只有是json格式的字符串才能使用这个转化方法
                            var phone_result = document.querySelector("#phone_result");
                            if(result.status == 0) {
                                // 代表手机号可用
                                phone_result.innerText = result.message.tips + "," + result.message.phonefrom;
                            } else if(result.status == 1){
                                // 代表手机号码不可用
                                phone_result.innerText = result.message;
                            }
                        }
                    }
                }

            }
        }
    </script>
</head>
<body>
    
    <h1>注册界面</h1>
    <form action="">
        用户名:<input type="text" id="username"><span id="username_result"></span><br>
        邮箱:<input type="text" id="email"><span id="email_result"></span><br>
        手机号:<input type="text" id="phone"><span id="phone_result"></span><br>
    </form>
</body>

此处发现验证唯一性的代码大量类似,应该想到封装的思想

同步和异步的理解

  1. 将Ajax请求改为同步请求
xhr.open("get","./server/checkUsername.php?uname=" + usernameValue,false);

这样做的话,会有两个问题:

第一:界面卡顿,卡顿多长时间,取决于网络速度
第二:xhr.onreadyStatechange 的回调函数不会被执行,需要修改代码才能获取到数据,将回调去掉即可
  1. 异步的底层原理

js中的异步的实现的原理是单线程加事件队列,js的代码执行是单线程的,所谓的单线程的含义是js的代码是从上往下按顺序依次执行的,一定是上一行代码执行完再执行下一行代码,事件队列可以认为是一个容器,这个容器中存储一些回调函数。这些回调函数只有在js代码全部执行完成之后,才有可能会去调用,因为js是单线程的,一次只能做一件事情。

sync_async 举例

在这里插入图片描述

Ajax 的执行步骤图解:

在这里插入图片描述

数据格式

  1. 什么是数据格式

将数据通过一定的规范组织起来,叫做数据格式,例如 张三%19%男-李四%23%男-王五%30%女 这就是一种数据格式,这种自定义的数据格式组成的规则不通用

  1. Xml 数据格式

Xml 数据格式是将数据以标签的方式进行组装,必须以 <?xml version="1.0" encoding="utf-8" ?> 开头,标签必须成对出现,也就是有开始标签就一定要有结束标签。xml 是一个通用的标准,任何人都知道该如何解析它。

在这里插入图片描述

缺点:体积太大,传输慢,元数据太多,解析不方便,目前使用的很少。

  1. Json 数据格式

Json 数据格式类似于js中的对象方式,通过key-value 的形式组装,是一个通用的标准,任何人都知道如何解析它。

在这里插入图片描述

Ajax 的使用——解析xml数据格式

<head>
	<meta charset="UTF-8">
	<title>书籍列表</title>
	<style>
		div{
			width: 800px;
			margin: 20px auto;
		}
		table{
			width: 800px;
			margin: 20px auto;
			border-collapse: collapse;
		}
		th{
			background-color: #0094ff;
			color:white;
			font-size: 16px;
			padding: 5px;
			text-align: center;
			border: 1px solid black;
		}
		td{
			padding: 5px;
			text-align: center;
			border: 1px solid black;
		}
	</style>
	<script>
		window.onload = function() {
			var xhr = new XMLHttpRequest();
			xhr.open("get","./server/getBooks.php",true);
			xhr.send(null);
			xhr.onreadystatechange = function() {
				if(xhr.readyState == 4) {
					if(xhr.status == 200) {
						var result = xhr.responseXML;					}
						console.log(result.getElementsByTagName("booklist")[0].getElementsByTagName("book"));
						var books = result.getElementsByTagName("booklist")[0].getElementsByTagName("book");
						var newHtml = document.getElementById("bookContainer").innerHTML;
						for(var i = 0; i < books.length; i++) {
							
							var itemBook = books[i];
							var name = itemBook.getElementsByTagName("name")[0].textContent;
							var author = itemBook.getElementsByTagName("author")[0].textContent;
							var desc = itemBook.getElementsByTagName("desc")[0].textContent;
							var tempHtml = "<tr><td>"+name+"</td><td>"+author+"</td><td>"+desc+"</td></tr>";
							// console.log(tempHtml);
							newHtml += tempHtml;
						}
						document.getElementById("bookContainer").innerHTML = newHtml;
				}
			}
		}
	</script>
</head>
<body>
	<div>
		<table id="bookContainer">
			<tr>
				<th>书名</th>
				<th>作者</th>
				<th>描述</th>
			</tr>
			<tr>
				
			</tr>
		</table>
	</div>
</body>

php 文件如下

<?php 
    header("Content-Type:text/xml;");//这里设置响应头信息,保证浏览器可以把相应内容识别为xml文件类型

    $arr = array();
    $arr[0] = array("name"=>"三国演义","author"=>"罗贯中","desc"=>"一个杀伐纷争的年代");
    $arr[1] = array("name"=>"水浒传","author"=>"施耐庵","desc"=>"108条好汉的故事");
    $arr[2] = array("name"=>"西游记","author"=>"吴承恩","desc"=>"佛教与道教斗争");
    $arr[3] = array("name"=>"红楼梦","author"=>"曹雪芹","desc"=>"一个封建王朝的缩影");
 ?>
<?xml version="1.0" encoding="utf-8" ?>
<booklist>
    <?php 
        foreach ($arr as $key => $value) {
    ?>
    <book>
        <name><?php echo $value['name'] ?></name>
        <author><?php echo $value['author'] ?></author>
        <desc><?php echo $value['desc'] ?></desc>
    </book>
    <?php 
        }
     ?>
</booklist>

Ajax 的使用——解析json数据格式

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>学生列表</title>
	<style>
		div{
			width: 800px;
			margin: 20px auto;
		}
		table{
			width: 800px;
			margin: 20px auto;
			border-collapse: collapse;
		}
		th{
			background-color: #0094ff;
			color:white;
			font-size: 16px;
			padding: 5px;
			text-align: center;
			border: 1px solid black;
		}
		td{
			padding: 5px;
			text-align: center;
			border: 1px solid black;
		}
	</style>
	<script>
		window.onload = function() {
			var xhr = new XMLHttpRequest();	
			xhr.open("get","./server/getStudents.php",true);
			xhr.send(null);

			xhr.onreadystatechange = function(){
				if(xhr.readyState == 4){
					if(xhr.status == 200){
						var result = xhr.responseText;
						// 这里的responseText 只是一个字符串 需要把JSON格式的字符串转化为对象之后才能方便我们进行值的获取
						result = JSON.parse(result);
						var newHtml = document.getElementById("container").innerHTML;
						for(var i =0; i < result.length; i++) {
							var item = result[i];
							var name = item.name;
							var sex = item.sex;
							var age = item.age;
							var tempHtml = "<tr><td>"+name+"</td><td>"+age+"</td><td>"+sex+"</td></tr>";
							newHtml += tempHtml;
							// console.log(newHtml);
						}
						document.getElementById("container").innerHTML = newHtml;
					}
				}
			};
		}
	</script>
</head>
<body>
	<div>
		<table id="container">
			<tr>
				<th>姓名</th>
				<th>年龄</th>
				<th>性别</th>
			</tr>
			
		</table>
	</div>
</body>
</html>

php 文件如下

<?php 
    $arr = array();
    $arr[0] = array("name"=>"张三","age"=>"19","sex"=>"男");
    $arr[1] = array("name"=>"李四","age"=>"23","sex"=>"男");
    $arr[2] = array("name"=>"王五","age"=>"30","sex"=>"女");

    echo json_encode($arr);
 ?>

封装Ajax

对于封装方法,我们主要考虑几个方面:

  1. 哪些东西是变的
  2. 哪些东西是不变的
  3. 如何将结果通知调用者
  4. 如何调用方便

根据之前的案例经验,我们知道,不同场景的ajax调用,调用方法get还是post这个是有可能发送改变的,调用url地址也是会变的,请求参数也是会变的,返回数据的类型也是会变的。对于发生改变的东西可以通过参数传递的方式实现。

基础代码例如创建 XMLHttpRequest 对象,准备发送,执行发送,响应回调中有些代码也是固定不变的。
将结果通知调用者也可以通过在调用时传入一个方法就可以实现。

综上所述,代码可以封装成以下形式;

function myAjax(type,url,params,dataType,callback,async) {
    // 1 2 3 4
    var xhr = null;
    if(window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
    }
    // 如果不传参 为null 得到的是 ?null 这里需要进行判断
    if(type == "get") {
        if(params && params != "") {
            url += "?" + params;
        }
    }
    xhr.open(type,url,async);
    if(type == "get") {
        xhr.send(null);
    } else if(type == "post") {
        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        xhr.send(params);
    }
    if(async) {
        xhr.onreadystatechange = function() {
        if(xhr.readyState == 4) {
            if(xhr.status == 200) {

                var result = null;
                if(dataType == "json") {
                    result = xhr.responseText;
                    result = JSON.parse(result);
                } else if(dataType == "xml") {
                    result = xhr.responseXML;
                } else {
                    result = xhr.responseText;
                }
                // 考虑 callback 存在
                if(callback){
                    callback(result);
                } 
            }
        }
        }
    } else {
        if(xhr.readyState == 4) {
            if(xhr.status == 200) {
                var result = null;
                if(dataType == "json") {
                    result = xhr.responseText;
                    result = JSON.parse(result);
                } else if(dataType == "xml") {
                    result = xhr.responseXML;
                } else {
                    result = xhr.responseText;
                }
                // 考虑 callback 存在
                if(callback){
                    callback(result);
                }
            }
        }
    } 
}

现在还差最后一个步骤,如何调用方便。对于目前的封装,我们可以很容易发现这个封装方法的两个缺点:
1. 参数的顺序不可以改变
2. 参数没有默认值,每次都得传递

这两个缺点可以通过一个小技巧就可解决,我们将封装的参数变为一个对象即可。得到如下代码:

function myAjax2(obj) {
    var defaults = {
        type:"get",
        url:"#",
        dataType:"json",
        data:{},
        async:true
    };
    // obj中的属性,覆盖到defaults中的属性
    // 1. 如果有一些属性只存在obj中,会给defaults中增加属性
    // 2. 如果有一些属性在obj和defaults中都存在,会将defaults中的默认值覆盖
    // 3. 如果有一些属性只在defaults中存在,在obj中不存在,这时候defaults中将保留预定义的默认值
    for(var key in obj) {
        defaults[key] = obj[key];
    }; 
}

优化过后的封装代码为

function myAjax2(obj) {
    var defaults = {
        type:"get",
        url:"#",
        dataType:"json",
        data:{},
        async:true,
        success:function(){console.log(result);}
    };
    // obj中的属性,覆盖到defaults中的属性
    // 1. 如果有一些属性只存在obj中,会给defaults中增加属性
    // 2. 如果有一些属性在obj和defaults中都存在,会将defaults中的默认值覆盖
    // 3. 如果有一些属性只在defaults中存在,在obj中不存在,这时候defaults中将保留预定义的默认值
    for(var key in obj) {
        defaults[key] = obj[key];
    };

    var xhr = null;
    if(window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
    }
    // 得到params
    // data:{
    //     uname:"zhangsan",
    //     age:"18"
    // } // uname=zhangsan&age=18
    var params = "";
    for(var attr in defaults.data){
        params += attr + "=" + defaults.data[attr] + "&";
    }
    if(params) {
        params = params.substring(0,params.length - 1);
    }
    if(defaults.type == "get"){
        defaults.url += "?" + params;
    }
    xhr.open(defaults.type,defaults.url,defaults.async);

    if(defaults.type == "get"){
        xhr.send(null);
    } else if (defaults.type == "post") {
        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        xhr.send(params);
    }

    if(defaults.async) {
        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4) {
                if(xhr.status == 200) {
                    if(defaults.dataType == "json") {
                        result = xhr.responseText;
                        result = JSON.parse(result);
                    } else if(defaults.dataType == "xml") {
                        result = xhr.responseXML;
                    } else {
                        result = xhr.responseText;
                    }
                    defaults.success(result);
                }
            }
        }
    } else {
        if(xhr.readyState == 4) {
            if(xhr.status == 200) {
                if(defaults.dataType == "json") {
                    result = xhr.responseText;
                    result = JSON.parse(result);
                } else if(defaults.dataType == "xml") {
                    result = xhr.responseXML;
                } else {
                    result = xhr.responseText;
                }
                defaults.success(result);
            }
        }
    }
    
}

然后 调用 Ajax2 完成注册页面案例:

    <script type="text/javascript" src="myutils.js"></script>
    <script>
        window.onload = function() {

            var username = document.querySelector("#username");
            var email = document.querySelector("#email");
            var phone = document.querySelector("#phone");

            username.onblur = function() {
                var usernameValue = username.value;
                var type = "get";
                var url = "./server/checkUsername.php";
                var params = "uname=" + usernameValue;
                var dataType = "text";

                myAjax2({
                    url:url,
                    // type:"get", 可以不写 默认为get
                    data:{uname:usernameValue},
                    dataType:"text",
                    success:function(result){
                        var username_result = document.querySelector("#username_result");
                        if(result == "ok") {
                            username_result.innerText = "用户名可用";
                        } else {
                            username_result.innerText = "用户名已被注册";
                        }
                    },
                    async:false
                });
            }
            
            email.onblur = function() {
                var emailValue = email.value;
                var type = "post";
                var url = "./server/checkEmail.php";
                var params = "e=" + emailValue;
                var dataType = "text";

                myAjax2({
                    url:url,
                    type:"post",
                    dataType:"text",
                    data:{
                        e:emailValue
                    },
                    success:function(result){
                        var email_result = document.querySelector("#email_result");
                        if(result == 0) {
                            // 邮箱可用
                            email_result.innerText = "邮箱可用";
                        } else {
                            // 邮箱不可用
                            email_result.innerText = "邮箱不可用";
                        }
                    },
                    async:true
                });


            phone.onblur = function() {
                var phoneValue = phone.value;
                var type = "post";
                var url = "./server/checkPhone.php";
                var params = "phonenumber=" +phoneValue;
                var dataType = "json";

                myAjax2({
                    url:url,
                    type:"post",
                    data:{
                        phonenumber:phoneValue
                    },
                    success:function(result){
                        var phone_result = document.querySelector("#phone_result");
                        if(result.status == 0) {
                            // 代表手机号可用
                            phone_result.innerText = result.message.tips + "," + result.message.phonefrom;
                        } else if(result.status == 1){
                            // 代表手机号码不可用
                            phone_result.innerText = result.message;
                        }
                    }
                });
            }}
        }
    </script>

<body>
    <h1>注册界面</h1>
    <form action="">
        用户名:<input type="text" id="username"><span id="username_result"></span><br>
        邮箱:<input type="text" id="email"><span id="email_result"></span><br>
        手机号:<input type="text" id="phone"><span id="phone_result"></span><br>
    </form>
</body>

jQuery 中使用ajax

对于jQuery中关于ajax的封装,它提供了很多方法供开发者调用。不过这些封装都是基于一个方法的基础上进行的修改。这个方法就是$.ajax()
我们主要关注以下3个方法:

  1. $.ajax()
    username.onblur = function() {
        var usernameValue = username.value;
        var type = "get";
        var url = "./server/checkUsername.php";
        var params = "uname=" + usernameValue;
        var dataType = "text";

        $.ajax({
            url:url,
            type:type,
            data:{
                uname:usernameValue
            },
            async:false,
            dataType:"text",
            success:function(result){
                var username_result = document.querySelector("#username_result");
                if(result == "ok") {
                    username_result.innerText = "用户名可用";
                } else {
                    username_result.innerText = "用户名已被注册";
                }
            }
        });
    }
  1. $.get()
username.onblur = function() {
    var usernameValue = username.value;
    var type = "get";
    var url = "./server/checkUsername.php";
    var params = "uname=" + usernameValue;
    var dataType = "text";

    $.get(url+"?"+params,function(result){
        console.log(result);
    });
}
  1. $.post()
email.onblur = function() {
    var emailValue = email.value;
    var type = "post";
    var url = "./server/checkEmail.php";
    var params = "e=" + emailValue;
    var dataType = "text";

    $.post(url,{e:emailValue},function(result){
        console.log(result);
    });
}

跨域

  1. 跨域的概念:

下面介绍一个知识叫做跨域,这个知识点是源于一个叫同源策略的东西。
同源策略是浏览器上为安全性考虑实施的非常重要的安全机制。Ajax默认是能获取到同源的数据,对于非同源的数据,ajax是获取不到的。

下面举一个例子,来看看什么叫同源
比如有一个页面,它的地址为 http://www.example.com.dir/page.html 这个网址,在这个网址中,要去获取服务器的数据,获取数据的地址如下所示:

在这里插入图片描述

所谓的同源就是协议、端口、域名三者都完全一样,如果我们使用ajax来请求非同源路径下的数据,那么将会报如下错误:

在这里插入图片描述

在之前的案例中,我们都能够成功的获取到服务器的数据,那是因为服务器的接口地址和前端界面都处于同源状态下。

那我们需要处理获取非同源数据获取的情况吗?答案是肯定的。因为前端界面访问非同源的服务器的这种需求是非常常见的,比如在前端界面中获取天气数据,天气数据肯定是存在于别人的服务器上的,我们如果不能使用ajax进行访问的话,那么该怎么办呢? 这里就需要用到跨域了。

所以,先把概念理解清楚,不管是ajax还是跨域,都是为了访问服务器数据。简单来说:Ajax 是为了访问自己服务器的数据,跨域是为了访问别人服务器的数据。

  1. 跨域的实现

XMLHttpRequest 对象默认情况下是无法获取到非同源服务器下的数据。那么怎么获取别人服务器的数据呢?使用 XMLHttpRequest 是达不到的,我们只能另辟蹊径。

我们可以通过script标签,用script标签的src属性引入一个外部文件,这个外部文件是不涉及到同源策略的影响的。

例如:

<script type="text/javascript" src="http://www.lisi.com/test.js"></script>
  • 引入外部js文件

  • 访问外部php文件

  • 动态创建script标签传入动态参数

  • 前端界面决定方法名称

  • 给window 增加属性进行方法定义

<!--<script>
        // 跨域的本质,其实就是服务器返回了一个方法调用,这个方法是我们事先定义好的,而方法中的参数就是我们想要的数据
        function foo2(data) {
            console.log(data);
        }
    </script> -->
    <!-- <script type="text/javascript" src="http://www.zhujin.com/data.php?city=beijing"></script> -->
    <script>
        window.onload = function() {
            var btn = document.querySelector("#btn");
            btn.onclick = function() {
                var cityName = document.querySelector("#city").value;
                // 动态创建script标签,动态指定src属性的值

                var script = document.createElement("script");
                script.src = "http://www.zhujin.com/data.php?city="+cityName+"&callback=foo2";

                // 这行代码就相当于注释掉的script部分代码 给window增加了一个属性
                window["foo2"] = function(data) {
                    console.log(data);
                };

                var head = document.querySelector("head");
                head.appendChild(script);
            };
        }
    </script>
</head>
<body>
    <h1>天气信息查询</h1>
    <input type="text" id="city" placeholder="请输入城市名称">
    <input type="button" id="btn" value="查询">
</body>

lisi 文件夹下的data.php文件代码如下:

<?php

    $cbName = $_GET["callback"];
    $city = $_GET["city"];
    if($city == "beijing") {
        echo $cbName."('北京天气🌤')";
    } else {
        echo $cbName."('没有查询到天气情况')";
    }
    // echo "var str = 'haha'";
?>

小案例:淘宝提示词接口、百度提示词接口

!()[D:\前端学习\images\41.png]

    <script>
        window.onload = function() {
            var btn = document.querySelector("#btn");
            btn.onclick = function() {
                var keywordValue = document.querySelector("#keyword").value;
                console.log(keywordValue);

                var script = document.createElement("script");
                script.src = "https://suggest.taobao.com/sug?q="+keywordValue+"&callback=haha";
                window["haha"] = function(data) {
                    var liTag = "";
                    for(var i=0;i<data.result.length;i++){
                        var temp = data.result[i];
                        var tempSug = temp[0];
                        liTag += "<li>" +tempSug+ "</li>"
                    }
                    var ulTag = document.querySelector("ul");
                    ulTag.innerHTML = liTag;
                    // console.log(data);
                };
                var head = document.querySelector("head");
                head.appendChild(script);
            }
        }
    </script>

<body>
    <input type="text" id="keyword" placeholder="请输入相关的关键字">
    <input type="button" id="btn" value="查询">

    <ul>
        
    </ul>
</body>

百度提示词案例:

在这里插入图片描述

    <script>
        window.onload = function() {
            var btn = document.querySelector("#btn");
            btn.onclick = function() {
                var keywordValue = document.querySelector("#keyword").value;
                console.log(keywordValue);

                var script = document.createElement("script");
                script.src = "http://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd="+keywordValue+"&cb=hehe";
                window["hehe"] = function(data) {
                    var liTag = "";
                    for(var i=0;i<data.s.length;i++){
                        var temp = data.s[i];
                        liTag += "<li>" + temp + "</li>";
                    }
                    var ulTag = document.querySelector("ul");
                    ulTag.innerHTML = liTag;
                    // console.log(data);
                };
                var head = document.querySelector("head");
                head.appendChild(script);
            }
        }
    </script>

</head>
<body>
    <input type="text" id="keyword" placeholder="请输入相关的关键字">
    <input type="button" id="btn" value="查询">

    <ul>
        
    </ul>
</body>

可以发现两个案例中出现了很多重复代码,可对其进行 跨域部分的封装

myutils.js 文件代码如下:

// "https://suggest.taobao.com/sug?q="+keywordValue+"&callback=haha"
// obj = {
//     url:"https://suggest.taobao.com/sug",
//     data:{q:"123",pwd:"123456"},
//     success:function(data){}
// }

function myAjax(obj) {
    var defaults = {
        type:"get",
        url:"#",
        data:{},
        success:function(data){},
        jsonp:"callback",
        jsonpCallback:"haha"
    };

    for(var key in obj) {
        defaults[key] = obj[key];
    }

    var params = "";
    for(var attr in defaults.data) {
        params += attr + "=" + defaults.data[attr] + "&";
    }
    if(params) {
        params = params.substring(0,params.length-1);
        defaults.url += "?" + params;
    }

    defaults.url += "&"+defaults.jsonp+"=" + defaults.jsonpCallback;
    console.log(defaults.url);

    var script = document.createElement("script");
    script.src = defaults.url;

    window[defaults.jsonpCallback] = function(data){
        defaults.success(data);
    };

    var head = document.querySelector("head");
    head.appendChild(script);

}

封装之后引用js文件即可:

    <script type="text/javascript" src="myutils.js"></script>
    <script>
        window.onload = function() {
            var btn = document.querySelector("#btn");
            btn.onclick = function() {
                var keywordValue = document.querySelector("#keyword").value;
                // console.log(keywordValue);
                myAjax({
                    url:"http://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su",
                    data:{wd:keywordValue},
                    success:function(data){
                        // console.log(data);
                        var liTag = "";
                        for(var i=0;i<data.s.length;i++){
                            var temp = data.s[i];
                            liTag += "<li>" + temp + "</li>";
                        }
                        var ulTag = document.querySelector("ul");
                        ulTag.innerHTML = liTag;
                    },
                    jsonp:"cb",
                    jsonpCallback:"xixi"
                });
            }
        }
    </script>

</head>
<body>
    <input type="text" id="keyword" placeholder="请输入相关的关键字">
    <input type="button" id="btn" value="查询">

    <ul>
        
    </ul>
</body>

使用jQuery获取跨域数据

    <script type="text/javascript" src="jquery.js"></script>
    <script>
        window.onload = function() {
            var btn = document.querySelector("#btn");
            btn.onclick = function() {
                var keywordValue = document.querySelector("#keyword").value;
                console.log(keywordValue);

                $.ajax({
                    url:"http://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su",
                    data:{wd:keywordValue},
                    success:function(data){
                        // console.log(data);
                        var liTag = "";
                        for(var i=0;i<data.s.length;i++){
                            var temp = data.s[i];
                            liTag += "<li>" + temp + "</li>";
                        }
                        var ulTag = document.querySelector("ul");
                        ulTag.innerHTML = liTag;
                    },
                    dataType:"jsonp",
                    jsonp:"cb",
                    // jsonpCallback:"haha" 这个就是改变调用的函数名称而已 
                });
            }
        }
    </script>

<body>
    <input type="text" id="keyword" placeholder="请输入相关的关键字">
    <input type="button" id="btn" value="查询">

    <ul>
        
    </ul>
</body>

模板引擎的使用

无论是Ajax还是跨域,目的都是为了获取服务器的数据,获取数据之后将前端界面进行渲染。怎么渲染前端界面呢?前端界面都是由标签构成的,所以前端界面的渲染主要做的就是生成html标签。

生成html标签我们可以通过拼接字符串的方式来实现。这种方式在标签结构比较复杂的情况之下很不好操作和后期的维护,并且容易出错。接下来就介绍一种技术叫做模板引擎,通过模板引擎我们可以很方便的生成html标签。

模板引擎的本质:将数据和模板结合起来生成html片段。所以模板引擎需要两个组成部分:模板和数据,通过数据,将模板指定的标签动态生成,方便维护。

常见的模板引擎有很多,这里介绍一个效率最高的模板引擎artTemplate,这是腾讯公司出品的开源的模板引擎,在github上可以下载到源代码。

使用步骤如下:

  1. 引入js文件

  2. 定义模板

  3. 将数据和模板结合起来生成html片段

  4. 将html片段演染到界面中

基本语法:

得到数据中的值{{value}}

循环操作 {{each result as value i}}{{/each}}

转义:#的使用{{#value}}

条件判断 {{if xxx}}{{/if}}

技巧:有时候有可能需要对原始数据进行加工操作

例如:

<script type="text/javascript" src="./template.js"></script>
    <!-- data.data -->
    <script id="test" type="text/html">
        <ul>
            {{each arr as value i}}
                <li>{{value}}</li>
            {{/each}}
        </ul>
    </script>

    <script>
        window.onload = function(){
            var data = ['文艺', '博客', '摄影', '电影', '民谣', '旅行', '吉他'];
            var temp = {};
            temp.arr = data;
            var html = template("test",temp);//data.xxx 
            document.querySelector("#content").innerHTML = html;
        }
    </script>
</head>

<body>
    <div id="content">
        <ul>
            <li>文艺</li>
            <li>博客</li>
        </ul>
    </div>
</body>

在这里插入图片描述

<!-- 1、模板的type  2、给模板配一个id
        data.s
    -->
    <script type="text/html" id="resultTemplate">
        <ul>
            {{each s as value i}}
                <li>
                    <div>
                        <span>结果{{i+1}}</span>
                        <span>{{value}}</span>
                    </div>
                </li>
            {{/each}}
        </ul>
    </script>
    <title>Document</title>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript" src="template.js"></script>
    <script>
        window.onload = function() {
            var btn = document.querySelector("#btn");
            btn.onclick = function() {
                var keywordValue = document.querySelector("#keyword").value;
                console.log(keywordValue);

                $.ajax({
                    url:"http://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su",
                    data:{wd:keywordValue},
                    success:function(data){
                        console.log(data);

                        // template方法的含义就是将数据和模板结合起来,生成html片段
                        var html = template("resultTemplate",data);
                        console.log(html);
                        var ulTag = document.querySelector("ul");
                        ulTag.innerHTML = html;
                        var divResult = document.querySelector("#resultDiv");
                        divResult.innerHTML = html;
                    },
                    dataType:"jsonp",
                    jsonp:"cb"
                });
            }
        }
    </script>

<body>
    <input type="text" id="keyword" placeholder="请输入相关的关键字">
    <input type="button" id="btn" value="查询">

    <div id="resultDiv">
        <ul>
            <li>
                <div>
                    <span>结果1:</span>
                    <span>1</span>
                </div>
            </li>
        </ul>
    </div>
</body>

最终运行结果为这样:

在这里插入图片描述

  1. 天气查询案例

接口文档如下:
在这里插入图片描述
在这里插入图片描述

完整源码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>获取天气信息</title>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript" src="template.js"></script>
    <script type="text/html" id="weatherTemplate">
    {{each weather as value i}}
        <div>
                <span>{{value.date}}</span>
                <ul>
                    <li>白天天气:{{value.info.day[1]}}</li>
                    <li>白天温度:{{value.info.day[2]}}</li>
                    <li>白天风向:{{value.info.day[3]}}</li>
                    <li>白天风速:{{value.info.day[4]}}</li>
                </ul>
                <ul>
                    <li>夜间天气:{{value.info.night[1]}}</li>
                    <li>夜间温度:{{value.info.night[2]}}</li>
                    <li>夜间风向:{{value.info.night[3]}}</li>
                    <li>夜间风速:{{value.info.night[4]}}</li>
                </ul>
                {{if value.info.dawn}}
                <ul>
                    <li>黎明天气:{{value.info.dawn[1]}}</li>
                    <li>黎明温度:{{value.info.dawn[1]}}</li>
                    <li>黎明风向:{{value.info.dawn[1]}}</li>
                    <li>黎明风速:{{value.info.dawn[1]}}</li>
                </ul>
                {{/if}}
        </div>
     {{/each}}
    </script>
    <script type="text/javascript">
        $(function(){
            $("#query").click(function(){
                //将用户所选择的城市信息的天气预报情况查询出来
                var code = $("#city").val();
                console.log(code);
                $.ajax({
                    url:"http://cdn.weather.hao.360.cn/sed_api_weather_info.php",
                    data:{
                        app:"360chrome",
                        code:code
                    },
                    jsonp:"_jsonp",
                    dataType:"jsonp",
                    success:function(data){
                        console.log(data);
                        var html = template("weatherTemplate",data);
                        $("#info").html(html);
                    }
                });
            });
        });
    </script>
     <style type="text/css">
        #container{
            width: 400px;
            min-height: 300px;
            background-color: lightgreen;
            margin: auto;
            padding: 10px;
            text-align: center;
        }
        ul{
            list-style: none;
            text-align: left;
        }
    </style>
</head>
<body>
   <div id="container">
        <select id="city">
            <option value="101010100">北京</option>
            <option value="101020100">上海</option>
            <option value="101280101">广州</option>
            <option value="101280601">深圳</option>
            <option value="101210101">杭州</option>
            <option value="101200101">武汉</option> 
            <option value="101110101">西安</option>
            <option value="101190401">苏州</option>
            <option value="101220101">合肥</option> 
            <option value="101220606">宿松</option>
            <option value="101220201">蚌埠</option>
            <option value="101270201">攀枝花</option>
            <option value="101310218">珊瑚岛</option> 
            <option value="101340102">台北</option>
        </select>
        <input type="button" value="查询" id="query">
        <div id="info">
            
        </div>
    </div>
</body>
</html>
  1. 手机号码查吉凶

使用 www.mob.com 提供的数据服务来得到我们想要的数据。
第三方接口的使用一般来说,都是需要注册、创建应用、申请appkey 这几个步骤,然后按照文档中的要求进行api接口的调用即可。

获取服务器数据小结:

在这里插入图片描述

首先需要获取API接口文档:

访问www.mob.com

进入mobAPI --找到手机号码查询吉凶
在这里插入图片描述

获得到了API信息。其中key的值需要用户申请,点击右上角申请之后会获得一个申请码即可使用该接口。

注意访问得到的仅仅是一个json数据,并没有回调函数,所以无法跨域得到数据,该接口与我们的地址非同源,也无法使用正常的ajax,所以我们先访问自己的服务器,再由服务器去访问此接口,返回数据给我们前端界面

完整源码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>获取手机吉凶信息</title>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">

        $(function(){
            $("#btn").click(function(){
                var phoneNumber = $("#phonenumber").val();
                console.log(phoneNumber);
                $.ajax({
                    url:"./server/getphoneinfo.php",
                    data:{mobile:phoneNumber},
                    dataType:"json",
                    success:function(data){
                        console.log(data);
                        if(data.retCode == 200) {
                            var info = data.result.conclusion;
                            document.querySelector("#result").innerHTML = info;
                        } else {
                            document.querySelector("#result").innerHTML = "信息获取失败,请检查输入";
                        }
                        
                    }
                });
            });
        });
        
    </script>
</head>
<body>
   <div id="container">
        <input type="text" id="phonenumber" placeholder="请输入手机号码">
        <input type="button" id="btn" value="查询">
        <span id="result">结果信息</span>
    </div>
</body>
</html>

getphoneinfo.php 文件代码:

<?php
    // 在php中,获取一个链接中的数据
    // 设置编码
    header("Content-Type:text/plain;charset=utf-8");
    // 拿到前端的数据
    $phoneNumber=$_GET["mobile"];
    // 使用curl进行网络数据访问
    $ch=curl_init();
    // 网络访问的url地址
    $url="http://apicloud.mob.com/appstore/lucky/mobile/query?key=216e720443bb8&mobile=".$phoneNumber;
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
    // 执行HTTP请求
    curl_setopt($ch,CURLOPT_URL,$url);
    // 得到数据
    $res=curl_exec($ch);
    echo $res;
  
?>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值