【Ajax编程】Ajax的基础知识


Ajax编程

一、传统网站中存在的问题

  • 网速慢的情况下,页面加时长,用户只能等待
  • 表单提交后,如果一项内容不合格,需要重新填写所有表单内容
  • 页面跳转,重新加载页面,造成资源浪费,增加用户等待时间

二、Ajax概述

01.基本概念

  • 它是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高用户浏览网站应用的体验
  • 局部更新页面的数据,而不是整体刷新
  • 在不刷新页面的情况下,向服务器端提交请求

02.应用场景

  • 页面上拉加载更多数据
  • 列表数据无刷新分页
  • 表单项离开焦点数据验证
  • 搜索框提示文字下拉列表

03.运行环境

  • 需要运行在网站环境中才能生效,不能直接打开本地页面

04.运行原理

  • Ajax相当于浏览器发送请求与接收响应的代理人,以实现:在不影响用户浏览页面的情况下,局部更新页面数据,从而提高用户体验

05.Ajax实现步骤

(1)创建Ajax对象
  • 使用内置对象

    var xhr = new XMLHttpRequest();
    
(2)告诉Ajax请求地址以及请求方式
xhr.open("请求方式","请求地址");
(3)发送请求
xhr.send();
(4)获取服务器端给客户端的相应数据
xhr.onload= function() {
    console.log(xhr.responseText);
}
  • xhr.responseText()即为服务器端响应的数据

06.服务器端响应的数据格式

  • 服务器端响应数据的格式:JSON对象

  • 但在http请求与响应的过程中,无论是请求参数还是响应内容都是以字符串类型进行传输的

    • 因此,如果传输的是对象格式,可以通过JSON.parse()方法,将JSON字符串转换成JSON对象
    xhr.onload = ()=>{
        let responseText = JSON.parse(xhr.responseText);
        console.log(responseText);
    }
    

07.请求参数的传递

(1)GET请求参数的传递(地址栏)
  • 需要拼接请求参数

    var params = "name=" + name + "&age=" + age ;
    
  • 在地址栏拼接请求参数

    xhr.open("get","请求地址?" + params);
    
(2)POST请求参数的传递(请求体)
  • 必须设置请求报文(请求参数格式的类型)

    报文主要存储请求参数

    xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    
  • 仍然需要拼接请求参数

    var params = "name=" + name + "&age=" + age ;
    
  • 设置请求方式和请求地址

    xhr.open("post","请求地址");
    
  • 然后发送请求

    xhr.send(params);
    
(3)请求参数的格式
  • 第一种:application/x-www-form-urlencoded

    name=Ruovan&age=24
    
  • 第二种:application/json

    {name:"Ruovan",age:"24"}
    
    • 在请求头中指定 Content-Type属性的值是 application/json,告诉服务器端当前请求参数的格式是json

    • 还需要将JSON对象转换为JSON字符串,放在 send()方法中,因为传递过程中,是以字符串形式传送的

      JSON.stringify(JSON对象);
      
  • 【注意】:get请求是不能提交json对象数据格式的,传统网站的表单提交也是不支持json对象数据格式的


08.获取服务端响应的另一种方式

  • onreadystatechange

  • 在创建ajax对象,配置ajax对象,发送请求,以及接收完服务器端响应数据,这个过程中的每个步骤都会对应一个数,这个数值就是ajax状态码

    • 0:请求未初始化,没有调用open()
    • 1:请求已经建立,但是还没有发送,没有调用send()
    • 2:请求已经发送
    • 3:请求正在处理中,通常响应中已经有部分数据可以用了
    • 4:响应已经完成,可以获取并使用服务器的响应了
    xhr.readyState;		//获取Ajax状态码
    
    // 当ajax状态码发生变化的时候触发
    xhr.onreadystatechange = ()=>{
        console.log(xhr.readyState);
    }
    

09.Ajax错误处理

  • 网络畅通,服务器端能接收到请求,服务器端返回的结果不是预期结果

    • 可以判断服务器端返回的状态码,分别进行处理
    • 使用xhr.status,以获取http状态码
  • 网络畅通,服务器端没有接收到请求,返回404状态码

    • 检查请求地址是否错误
  • 网络畅通,服务器端能接收到请求,服务器端返回500状态码

    • 服务器端错误
  • 网络中断,请求无法发送到务器端

    • 会触发xhr对象下面的 onerror 事件,在 onerror 事件处理函数中对错误进行处理

      xhr.onerror = ()=>{
        console.log("网络中断");
      };
      

10.低版本浏览器的缓存问题

  • 问题:在低版本的浏览器中,Ajax请求有严重的缓存问题

    • 即在请求地址不发生变化的情兄下,只有第一次请求会真正发送到服务器端
    • 后续的请求都会从浏览器的缓存中获取结果
    • 即使服务器端的数据更新了,客户端依然拿到的是缓存中的旧数据
  • 解决方案:在请求地址的后面添加请求参数,保证每次请求中的请求参数的值不相同,这样就不会从浏览器的缓存中获取数据

    xhr.open("get","请求地址?t=" = Math.random());
    

11.同步异步概述

  • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
  • Ajax请求属于异步代码

12.Ajax封装

  • 问题:发送一次请求代码过多,发送多次请求代码冗余且重复

  • 解决方案:将请求代码封装到函数中,发请求时调用函数即可

    function ajax(options){
        // 设置默认值
        var defaults = {
        	type: "get",
            url: "",
            data: {},
            header: {
                "content-Type": "application/x-www-form-urlencoded"
            },
            success: function(){},
            error: function(){}
    	};
    	// 浅拷贝,如果设置了对应属性的值,则使用该值,没有设置属性值,则使用默认值
    	Object.assign(defaults,options);
    	// 将下面的所有options对象都使用defults对象代替,因为options对象里面的属性已经拷贝给了defaults对象
    
        // 创建ajax对象
        var xhr = new XMLHttpRequest();
        // 拼接请求参数的对象
        var params = "";
        for(var attr in defaults.data){
            // 将参数转换为字符串格式
            params += attr + "=" + defaults.data[attr] + "&";
        }
        // 截取字符串,取消最后一个 & 符号
        params = params.substr(0, params.length-1);
        // 判断请求方式
        if(defaults.type == "get" ){
            // 如果是GET请求,就将参数拼接在请求地址中
            defaults.url = defaults.url + "?" + params;
        }
        // 配置ajax对象
        xhr.open(defaults.type, defaults.url);
        // 发送请求
        if(defaults.type == "post"){
            // 用户希望向服务器端传递的请求参数的类型
            var contentType = defaults.header["Content-Type"];
            // post请求必须设置请求报文
            xhr.setRequestHeader("Content-Type",contentType);
            // post请求要send参数
            // 判断类型
            if(contentType == "application/json"){
                // 如果是JSON格式,需要将传进来的JSON对象,转换为JSON字符串
                xhr.send(JSON.stringify(defaults.data));
            }else{
                // 传递拼接的字符串参数
             	xhr.send(params);
            }
        }else{
            xhr.send();
        }
        // 监听onload事件
        xhr.onload = function(){
            // xhr.getResponseHeader(),获取响应头中的数据
            var contentType = xhr.getResponseHeader("Content-Type");
            // 获取服务器端返回的数据
            var responseText = xhr.responseText;
            // 如果响应类型中包含application/json,即为JSOn数据格式
            if(contentType.include("application/json")){
                // 将从服务器端响应得到的数据,从JSON字符串格式转换为JSON对象格式
                responseText = JSON.parse(responseText);
            }
            // 判断请求是否成功
            if( xhr.status == 200){
            	defaults.success(responseText, xhr);    
            }else{
                defaults.error(responseText, xhr);
            }
        }
        
    }
    
    // 调用ajax函数发送ajax请求
    ajax({
        // 请求方式
        type: "get",
        // 请求地址
        url: "URL",
        // 请求参数
        data: {
            name: "Ruovan", 
            age: 24
        },
        // 请求报文类型
        header: {
        	"Content-Type": "application/x-www-form-urlencoded"
    	},
        // 请求成功
        success: function(data) {
            console.log(data);
        },
        // 请求失败
        error: function(data, xhr){
            console.log(data);
            console.log(xhr);
        }
    });
    
  • 注意事项:

    • 请求参数的位置问题:判断是什么请求方式以处理参数的传递
    • 请求参数的格式问题:传递对象数据类型
    • 请求参数的类型问题:将参数类型作为对象的一个属性,单独传入
    • 默认配置:在函数体内设置一个对象存储默认值,在调用函数时,采用assign浅拷贝的方式赋值给默认对象

三、模板引擎

01.下载使用

  • 引入template-webJS文件

  • 准备模板

    <script id="tpl" type="text/html">
    
    </script>
    
    • 必须定义id属性,作为模板的唯一标识,因为同一个页面可能会有多个模板
    • 因为需要在script标签内部写html语法,需要将type属性设置为text/html,可以正常解析、高亮显示
  • 拼接数据

    var html = template("tpl", {...});
    
    • template传入两个参数:模板id,需要拼接的数据
  • 将拼接好的html字符串添加到页面

    document.querySelector("target").innerHTML = html
    
  • 通过模板语法告诉模板引擎,数据和HTML字符串如何拼接(使用方法同nodejs的模板语法)

    <script id="tpl" type="text/html">
    	<section>
        	{{ data }}
        </section>
    </script>
    

四、FormData对象

01.作用

  • 模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式
  • 异步上传 二进制文件

02.使用

  • 准备HTML表单

  • 使用FormData构造函数,将HTML表单转化为formData对象

    var form = document.querySelector("#form");
    var formData = new FormData(form);
    
  • 提交表单对象

    xhr.send(formData);
    
  • 服务器端处理数据:

    • 不能使用body-parser模块处理formData对象的数据

    • 需要使用formidable模块

      // 创建formidable表单解析对象
      const form = new formidable.IncomingForm();
      // 解析客户端传递过来的FormData对象
      form.parse(req, (err, fileds, files) => {
          res.send(fileds);
      });
      
      

03.方法

  • 获取

    formData.get("属性名");
    
  • 设置

    formData.set("属性名", "属性值");
    
    • 属性存在则更改
    • 属性不存在则创建
  • 删除

    formData.delete("属性名");
    
  • 追加

    formData.append("属性名", "属性值");
    
    • 与设置set的区别
      • set覆盖已有值
      • append保留原有属性及其属性值,并新增一个相同属性

五、二进制文件

01.上传

  • 获取文件元素

    var file = document.querySelector("#file");
    // 注意,获取  file  元素,是一个集合
    
  • 触发文件选择事件

    file.onchange = function(){
        
    }
    
  • 创建空表单对象

    file.onchange = function(){
        var formData = new FormData();
    }
    
  • 将用户选择的文件追加到formData表单对象中

    formData.append("fileName",this.file[0]);
    // 这个 fileName 是自定义的属性名
    
  • Ajax处理

    var xhr = new XMLHttpRequest();
    // 请求方式必须是POST
    xhr.open("post","URL");
    
    xhr.send(formData);
    
    xhr.onload();
    
  • 服务器端处理

    // 创建formidable表单解析对象
    const form = new formidable.IncomeForm();
    // 设置客户端上传文件的存储路径
    form.uploadDir = path.join(__dirname, "public", "uploads");
    // 保留上传文件的后缀名,默认是没有后缀的
    form.keepExtensions = true;
    // 解析FormDatad对象
    form.parse(req, (err, fileds, files) => {
        res.send(fileds);
    });
    

02.上传进度

  • 持续触发

    xhr.upload.onprogress = function(ev){
        // 计算进度条宽度,
        // ev.loaded是文件已上传多少,ev.total是上传文件的总大小
        let result = (ev.loaded / ev.total) * 100 + "%";
        // 设置进度条宽度及内容
    }
    

03.图片即时预览

  • 获取文件路径

    通过上传文件时设置的文件属性名fileName

    这个属性名存储在 files

    // 解析FormDatad对象
    form.parse(req, (err, fileds, files) => {
        let filePath = files.fileName.path;	
        //该路径是绝对路径,需要分割
        filePath = filePath.split("public")[1];	// "/public/uploads/..."
        // 通过对象形式返回 
        res.send({
            path: filePath
        });
    });
    
  • 客户端处理

    xhr.onload = function(){
        if(xhr.status ==200 ){
            // 获取文件路径,对象化
            var result = JSON.parse(xhr.responseText);
            // 动态创建img表单
            var img = document.createElement("img");
            img.src = result.path;
            // 获取图片容器
            var box = documnet.querySelector("#box"); 
            // 加载完成后显示
            img.onload = function(){
                box.appendChild(img);
            }
        }
    }
    

六、Ajax请求限制

01.限制

  • Ajax只能向自己的服务器发送请求
  • 浏览器的同源政策的限制:无法向非同源地址发送Ajax请求,如果请求,浏览器就会报错

02.同源政策

  • 如果两个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源

  • 同源政策的目的

    • 同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据
    • 最初的同源政策是指A网站在客户端设置的Cookie,B网站是不能访问的

03.解决办法

  • 使用 JSONP解决同源限制问题

    • JSON with Padding
    • 它不属于Ajax请求,但它可以模拟Ajax请求
    • JSON 是一种数据格式, JSONP是实现跨域请求的一种方式

  • 不同源的服务器端请求地址写在 script标签的src属性中

    src属性不受同源政策的影响,可以向非同源端发送请求

  • 服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数

  • 在客户端全局作用于下定义函数func,且需要放在script标签前面

  • 在函数内部对服务器端返回的数据进行处理

  • 代码示例

    • 客户端

      <!-- 1.定义一个全局函数func,-->
      <script>
      	function func(data){
              
          }
      </script>
      <!-- 2.引入另一个源的服务器请求地址,(当前源的端口是3000) -->
      <!-- 当这个script标签加载完成,就会向另一个源发送请求,然后接受服务器的返回结果,调用函数-->
      <script src="http://localhost:3001/test"></script>
      
    • 服务器端

      app.get("/test",(req,res)=>{
          // 这里不能调用func,因为未定义,需要将 函数调用 写成字符串形式
          const fn = "func()";
          res.send(fn);
      })
      

04.代码优化

  • script请求变为动态请求

    • 点击按钮,动态创建script标签,并设置src属性
    • 监听script加载完成事件onload,事件加载完成后,就删除script标签
  • 函数名称问题:客户端和服务端需要一致

    • 将客户端的函数名作为参数传递给服务器端:

      // http://localhost:3001/test?callback=func
      app.get("test",(req,res)=>{
          const fnName = req.query.callback;
          // 转换数据为对象字符串
          const data = JSON.stringify({...});
          // 传递数据给函数调用
          const fn = fnName + "(" + data + ")";
          res.send(fn);
      })
      
      app.get("test",(req,res)=>{
      	// express框架下,res提供了一个jsonp方法,以实现上面的功能
          res.jsonp({...});
      })
      
  • 封装JSONP函数

    function jsonp(options){
        // 设置随机函数名,生成随机数,转换字符串,替换小数点
        let funName = "myJsonp" + Math.random().toSting().replace(".","")
        // 设置为全局函数,注意:不能window.fnName,否则会创建新的属性,变量只能用[]
        window[funName] = options.success;
        // 设置拼接数据中的数据为字符串
        let params = "";
        for(let attr in options.data){
            params += "&" + attr + "=" + options.data[attr];
        }
        // 动态创建script标签
        let script = document.createElement("script");
        // 设置src属性,并添加函数参数和请求数据
        script.src = options.url + "?callback=" + funName + params;
        // 加入页面
        let body = document.querySelector("#body");
        body.append(script);
        // 删除script标签
        script.onload = function(){
            body.removeChild(script);
        }
    }
        
    jsonp({
        // 传入请求地址
        url: "http://localhost:3000/test",
        data: {
            
        },
        success: function(){
            // 请求成功函数
            
        }
    })
    

05.CORS跨域资源共享

  • CORS:全称为 Cross-origin resource sharing,即跨域资源共享(即非同源)

    • 它允许浏览器向跨域服务器发送Ajax请求,克服了Ajax只能同源使用的限制

    • 客户端请求:

      origin: http://localhost:3000
      
    • 服务器端响应:

      Access-Control-Allow-origin: "http://localhost:3000"
      Access-Control-Allow-Origin: "*"
      
      • *代表允许所有客户端访问
  • 语法示例

    app.use((req,res,next)=>{
        // *号,代表允许所有客户端的访问,
        res.header("Access-Control-Allow-Origin","*");
        // 设置客户端的请求方式
        res.header("Access-Control-Allow-Methods","get,post");
        
        next(); 
    });
    

06.服务器端解决方案

  • 同源政策是浏览器给予Ajax技术的限制,服务器端不存在同源政策限制

  • 首先让1号客户端1号服务器端发送请求

  • 然后由1号服务器端访问2号服务器端的数据

  • 最后让1号服务器端获取的数据响应给1号客户端

    • 需要引入第三方模块request

      // 引入模块
      const request = require("request"); 
      // 返回一个方法
      
      app.get("/server",(req,res)=>{
          request("2号服务器端地址",(err, response, body) => {
               res.send(body);
          });
      });
      

      由1号客户端向1号服务器端发送请求,请求地址是/server

      然后在1号服务器端中调用request()方法,向2号服务器端发送请求,以获取数据

      2号服务器端作出响应给1号服务器端,再由1号服务器响应给1号客户端

07.cookies

  • 实现客户端与服务器端的身份识别

  • 但在跨域时,cookies不会随请求发送

    • withCredentials:在响应头设置指定在涉及到跨域请求时,是否携带 cookie 信息,默认值为 false

    • Access-Control-Allow-Credentials:true:允许客户端发送请求时携带 cookie

  • 语法示例:

    xhr.withCredentials = true;
    
    res.header("`Access-Control-Allow-Credentials","true");
    

七、$.ajax()方法

  • 调用$.ajax()方法,需要传递一个对象参数

    $.ajax({
        type: "get",
        url: "URL",
        // 自动转换为字符串形式:name=Ruovan&age=24
        data: {
            name: "Ruovan",
            age: 24
        },
        // application/json
        contentType: "application/x-www-form-urlencoded",
        beforeSend: function(){
            return false;
        },
        // 根据服务器端在响应头设置的数据的类型,自动转换成对应数据类型
        success: function(response){
            
        },
        error: function(xhr){
            
        }
    });
    
    • $.ajax()也可以发送jsonp请求

      $.ajax({
          url: "/jsonp",
          // 代表现在要发送的是jsonp请求
          dataType: "jsonp",
          // 修改callback参数名称,即请求地址后面的?callback=fn,更改为?cb=fn
          jsonp: "cb",
          // 指定函数名称
          jsonpCallback: "fnName",
          success: function(response){
              
          }
      });
      
      app.get("/jsonp",(req,res) => {
          const cb = req.query.cb;
          const data = cd + "()";
          res.send(data);
      })
      
  • serialize方法

    • 表单中的数据自动拼接成字符串类型的参数

      var params = $("#form").serialize();
      
    • 封装函数:将表单中用户输入的内容转换为对象类型

      function serializeObject(obj){
          // obj是表单jQuery对象
          // serializeArray()方法将页面中表单元素的内容转换为一个数组集合
          // 例:[{name:"username",value:"Ruovan"},{name:"age",value:"24"}]
          var params = obj.serializeArray();
          // 设置变量存储数据
          var result = {};
          // 遍历集合,取出数据,存储为对象类型
          $.each(params, function(index, value){
              // index:索引号;
              // value:对象  {name:"username",value:"Ruovan"}
              // 需要将属性名和属性值取出来,存储在result中
              result[value.name] = value.value;
          });
          return result;
      }
      
      
  • $.get()$.post()方法

    • 用于发送GET、POST请求
    $.get("url",{...},function(response){
        
    });
    // 第二个参数是可选参数,用于传递数据
    
  • Ajax全局事件

    • 只要页面中有Ajax请求被发送,对应的全局事件就会被触发
    • .ajaxStrat().ajaxComplete()
    // 当页面中有ajax请求发送时触发
    $(document).on("ajaxStart",function(){
        NProgress.start();
    });
    
    // 当页面中有ajax请求完成时触发(无论请求成功还是失败)
    $(document).on("ajaxComplete",function(){
        NProgress.done();
    });
    
    • NProgress插件:显示进度条

      • 引入cssjs文件

        <link rel="stylesheet" href="nprogress.css">
        <script src="nprogress.js"></script>
        
      • 调用方法:

        NProgress.start();	//进度条开始运动
        NProgress.done();	//进度条结束运动
        

八、RESTful API

01.一套关于设计请求的规范

GET:获取数据

POST:添加数据

PUT:更新数据

DELETE:删除数据

九、XML基础(略)

01.基本概念

  • XML的全称是 extensible markup language ,代表可扩展标记语言,它的作用是传输和存储数据

02.XML DOM

  • XML DOM即XML文档对象模型,是w3c组织定义的套操作XML文档对象的API,浏览器会将ⅩML文档解析成文档对象模型
app.get("/xml",(req,res)=>{
     res.header("content-type","text/xml");
    res.send();
});
<script>
    xhr.onload = function(){
		let xmlDocument = xhr.responseXML;
        // 与HTML的DOM操作类似
    }
</script>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值